Merge pull request #1592 from reedrosenbluth/feat/stx-balance

Clarity function 'stx-get-balance' for retrieving balance of STX address
This commit is contained in:
Reed Rosenbluth
2020-05-18 08:45:29 -06:00
committed by GitHub
9 changed files with 90 additions and 4 deletions

View File

@@ -164,7 +164,7 @@ impl <'a, 'b> ReadOnlyChecker <'a, 'b> {
ConsSome | ConsOkay | ConsError | DefaultTo | UnwrapRet | UnwrapErrRet | IsOkay | IsNone | Asserts |
Unwrap | UnwrapErr | Match | IsErr | IsSome | TryRet |
ToUInt | ToInt | Append | Concat | AsMaxLen |
ListCons | GetBlockInfo | TupleGet | Len | Print | AsContract | Begin | FetchVar | GetTokenBalance | GetAssetOwner => {
ListCons | GetBlockInfo | TupleGet | Len | Print | AsContract | Begin | FetchVar | GetStxBalance | GetTokenBalance | GetAssetOwner => {
self.check_all_read_only(args)
},
AtBlock => {

View File

@@ -350,6 +350,14 @@ impl TypedNativeFunction {
TypeSignature::UIntType,
TypeSignature::IntType],
BUFF_32.clone()))),
GetStxBalance =>
Simple(SimpleNativeFunction(FunctionType::Fixed(FixedFunction {
args: vec![
FunctionArg::new(TypeSignature::PrincipalType,
ClarityName::try_from("owner".to_owned())
.expect("FAIL: ClarityName failed to accept default arg name")),
],
returns: TypeSignature::UIntType }))),
StxTransfer =>
Simple(SimpleNativeFunction(FunctionType::Fixed(FixedFunction {
args: vec![

View File

@@ -136,8 +136,9 @@ fn test_impl_trait(){
#[test]
fn test_stx_ops(){
let good = ["(stx-burn? u10 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)",
"(stx-transfer? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)"];
let expected = [ "(response bool uint)", "(response bool uint)" ];
"(stx-transfer? u10 tx-sender 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)",
"(stx-get-balance 'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G)"];
let expected = [ "(response bool uint)", "(response bool uint)", "uint" ];
let bad = [
"(stx-transfer? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
@@ -148,6 +149,8 @@ fn test_stx_ops(){
"(stx-burn? 4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
"(stx-burn? u4 true)",
"(stx-burn? u4 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
"(stx-get-balance true)",
"(stx-get-balance 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)"
];
let bad_expected = [ CheckErrors::IncorrectArgumentCount(3,2),
CheckErrors::TypeError(UIntType, IntType),
@@ -156,7 +159,9 @@ fn test_stx_ops(){
CheckErrors::IncorrectArgumentCount(2,1),
CheckErrors::TypeError(UIntType, IntType),
CheckErrors::TypeError(PrincipalType, BoolType),
CheckErrors::IncorrectArgumentCount(2,3) ];
CheckErrors::IncorrectArgumentCount(2,3),
CheckErrors::TypeError(PrincipalType, BoolType),
CheckErrors::IncorrectArgumentCount(1,2) ];
for (good_test, expected) in good.iter().zip(expected.iter()) {
assert_eq!(expected, &format!("{}", type_check_helper(&good_test).unwrap()));

View File

@@ -230,6 +230,13 @@ pub const BLOCK_INFO: SimpleCostSpecification = SimpleCostSpecification {
read_count: Constant(1),
read_length: Constant(1) };
pub const STX_BALANCE: SimpleCostSpecification = SimpleCostSpecification {
write_length: Constant(0),
write_count: Constant(0),
runtime: Constant(1),
read_count: Constant(1),
read_length: Constant(1) };
pub const STX_TRANSFER: SimpleCostSpecification = SimpleCostSpecification {
write_length: Constant(1),
write_count: Constant(1),

View File

@@ -1183,6 +1183,19 @@ one of the following error codes:
"
};
const STX_GET_BALANCE: SimpleFunctionAPI = SimpleFunctionAPI {
name: None,
signature: "(stx-get-balance owner)",
description: "`stx-get-balance` is used to query the STX balance of the `owner` principal.
This function returns the STX balance of the `owner` principal. In the event that the `owner`
principal isn't materialized, it returns 0.
",
example: "
(stx-get-balance 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR) ;; returns u100
"
};
const STX_TRANSFER: SimpleFunctionAPI = SimpleFunctionAPI {
name: None,
signature: "(stx-transfer? amount sender recipient)",
@@ -1293,6 +1306,7 @@ fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
TransferToken => make_for_special(&TOKEN_TRANSFER, name),
TransferAsset => make_for_special(&ASSET_TRANSFER, name),
AtBlock => make_for_special(&AT_BLOCK, name),
GetStxBalance => make_for_simple_native(&STX_GET_BALANCE, &GetStxBalance, name),
StxTransfer => make_for_simple_native(&STX_TRANSFER, &StxTransfer, name),
StxBurn => make_for_simple_native(&STX_BURN, &StxBurn, name),
}

View File

@@ -20,6 +20,23 @@ macro_rules! clarity_ecode {
}
}
pub fn special_stx_balance(args: &[SymbolicExpression],
env: &mut Environment,
context: &LocalContext) -> Result<Value> {
check_argument_count(1, args)?;
runtime_cost!(cost_functions::STX_BALANCE, env, 0)?;
let owner = eval(&args[0], env, context)?;
if let Value::Principal(ref principal) = owner {
let balance = env.global_context.database.get_account_stx_balance(&principal);
Ok(Value::UInt(balance))
} else {
Err(CheckErrors::TypeValueError(TypeSignature::PrincipalType, owner).into())
}
}
pub fn special_stx_transfer(args: &[SymbolicExpression],
env: &mut Environment,
context: &LocalContext) -> Result<Value> {

View File

@@ -84,6 +84,7 @@ define_named_enum!(NativeFunctions {
TransferAsset("nft-transfer?"),
MintAsset("nft-mint?"),
MintToken("ft-mint?"),
GetStxBalance("stx-get-balance"),
StxTransfer("stx-transfer?"),
StxBurn("stx-burn?"),
});
@@ -160,6 +161,7 @@ pub fn lookup_reserved_functions(name: &str) -> Option<CallableType> {
GetTokenBalance => SpecialFunction("special_get_balance", &assets::special_get_balance),
GetAssetOwner => SpecialFunction("special_get_owner", &assets::special_get_owner),
AtBlock => SpecialFunction("special_at_block", &database::special_at_block),
GetStxBalance => SpecialFunction("special_stx_balance", &assets::special_stx_balance),
StxTransfer => SpecialFunction("special_stx_transfer", &assets::special_stx_transfer),
StxBurn => SpecialFunction("special_stx_burn", &assets::special_stx_burn),
};

View File

@@ -106,6 +106,7 @@ fn execute_transaction(env: &mut OwnedEnvironment, issuer: Value, contract_ident
fn test_native_stx_ops(owned_env: &mut OwnedEnvironment) {
let contract = "(define-public (burn-stx (amount uint) (p principal)) (stx-burn? amount p))
(define-public (xfer-stx (amount uint) (p principal) (t principal)) (stx-transfer? amount p t))
(define-read-only (balance-stx (p principal)) (stx-get-balance p))
(define-public (to-contract (amount uint) (p principal))
(let ((contract-principal (as-contract tx-sender)))
(stx-transfer? amount p contract-principal)))
@@ -210,6 +211,25 @@ fn test_native_stx_ops(owned_env: &mut OwnedEnvironment) {
&symbols_from_values(vec![Value::UInt(2), p2.clone(), p1.clone()])).unwrap_err(),
RuntimeErrorType::ArithmeticOverflow.into());
// test 6: check balance
let (result, _asset_map, _events) = execute_transaction(
owned_env, p2.clone(), &token_contract_id, "balance-stx",
&symbols_from_values(vec![p2.clone()])).unwrap();
assert_eq!(result, Value::UInt(1000));
// test 7: check balance is 0 for nonexistent principal
let sp_data = PrincipalData::parse_standard_principal("SPZG6BAY4JVR9RNAB1HY92B7Q208ZYY4HZEA9PX5").unwrap();
let nonexistent_principal = Value::Principal(PrincipalData::Standard(sp_data));
let (result, _asset_map, _events) = execute_transaction(
owned_env, p2.clone(), &token_contract_id, "balance-stx",
&symbols_from_values(vec![nonexistent_principal.clone()])).unwrap();
assert_eq!(result, Value::UInt(0));
// now, let's actually do a couple transfers/burns and check the asset maps.
let (result, asset_map, _events) = execute_transaction(
@@ -254,6 +274,18 @@ fn test_native_stx_ops(owned_env: &mut OwnedEnvironment) {
.get(&AssetIdentifier::STX()).unwrap(),
&AssetMapEntry::STX(10));
// now check contract balance with stx-get-balance
let cp_data = PrincipalData::parse_qualified_contract_principal("SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR.tokens").unwrap();
let contract_principal = Value::Principal(cp_data);
let (result, _asset_map, _events) = execute_transaction(
owned_env, p2.clone(), &token_contract_id, "balance-stx",
&symbols_from_values(vec![contract_principal.clone()])).unwrap();
assert_eq!(result, Value::UInt(10));
// now let's do a contract -> user transfer
let (result, asset_map, _events) = execute_transaction(

View File

@@ -87,6 +87,7 @@ pub fn get_simple_test(function: &NativeFunctions) -> &'static str {
TransferToken => "(ft-transfer? ft-foo u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
TransferAsset => "(nft-transfer? nft-foo 1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
AtBlock => "(at-block 0x0000000000000000000000000000000000000000000000000000000000000000 1)",
GetStxBalance => "(stx-get-balance 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
StxTransfer => "(stx-transfer? u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
StxBurn => "(stx-burn? u1 'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR)",
}