rustblockchainmovesmartcontractsaptos

Get signer of a contract inside a function


I need to somehow transfer APT coins to another account from smart contract itself. Here is an issue, because transfer function needs &signer of an sender and that's why I can not send it from contract.

Is there any chance that I can send APT directly from contract itself inside a function by getting &signer somehow, or without it? I've read a lot of docs, search for it on GitHub and found nothing. I would appreciate any type of help. Regards!


Solution

  • These days the easiest way to do this would be with an object.

    1. Create an object. Learn more here.
    2. Create an account alongside that object. Learn more here.
    3. Send coins from the object (assuming you have sent coins to it already).

    Overall your code to create the object + account would look like this:

    entry fun create(caller: &signer) {
      // Create an object.
      let caller_address = signer::address_of(caller);
      let constructor_ref = object::create_object(caller_address);
      let object_address = object::address_from_constructor_ref(&constructor_ref);
    
      // Create an account alongside the object.
      aptos_account::create_account(object_address);
    
      // Store an ExtendRef alongside the object.
      let object_signer = object::generate_signer(&constructor_ref);
      let extend_ref = object::generate_extend_ref(&constructor_ref);
      move_to(
        &object_signer,
        MyRefs { extend_ref: extend_ref },
      );
    }
    

    You'll see we store this MyRefs struct alongside the object. This is necessary to store the ExtendRef, which we need to generate a signer later so we can transfer coins. It looks like this:

    struct MyRefs has key, store {
      extend_ref: ExtendRef,
    }
    

    The code to transfer funds from the account would look like this:

    entry fun transfer(caller: &signer, obj: Object<Account>, to: address, amount: u64) acquires MyRefs {
      let caller_address = signer::address_of(caller);
      assert!(object::is_owner(obj, caller_address), error::permission_denied(CALLER_NOT_OWNER));
      let obj_address = object::object_address(&obj);
      let my_refs = borrow_global<MyRefs>(obj_address);
      let object_signer = object::generate_signer_for_extending(&my_refs.extend_ref);
      aptos_account::transfer(&object_signer, to, amount);
    }
    

    This transfer function takes in an Object<Account>. This assumes you know the object address somehow, e.g. from an API based on indexing. You could store the Object (which is essentially a reference) in a resource on chain attached to your account (e.g. MyObjectPointerStore) instead if you wanted.

    You can see the full code for this example here: https://github.com/banool/move-examples/tree/main/object_account.