2

I have two contracts.

Contract A is an ERC20 token with initial_supply all owned by the contract itself.

And Contract B which exposes a deposit method.

When a user sends a transaction to ContractB.deposit() I want it to:

  1. Send Contract A coins to contract B (from contract B i.e, contract B needs to request the coins from contract A to B)<- not sure how to do
  2. Send Contract A coins which are now stored in contract B to msg.sender <- not sure how to do

How can I achieve this? My own attempts have largely failed.

  • Hi Christian! Welcome to Ethereum Stackexchange! Who owns the token in 1? To make contract X request something from Y it will have to call a function from Y, then contract Y can send the anwer to X. If you provide more details about 1 and 2 I'll try to make some code to explain in detail. – Ismael Nov 16 '21 at 12:27
  • Hi Ismael! Thanks for the warm welcome :) I own token contract 1 (I initialised it) and I also own token contract 2 - I hope that answers your question – Christian Papathanasiou Nov 16 '21 at 14:07
  • The question was who owns the tokens that B wants to transfer. B only has access to its own tokens. The question isn't about contract's ownership, but who has the minted tokens. – Ismael Nov 16 '21 at 16:41
  • The contract itself owns the coins (inital_supply = address(self)) – Christian Papathanasiou Nov 16 '21 at 17:24
  • Take a look at the answer to this question https://ethereum.stackexchange.com/questions/46457/send-tokens-using-approve-and-transferfrom-vs-only-transfer, depending on who owns the tokens either use transfer or approve+transferFrom. – Ismael Nov 16 '21 at 21:29

2 Answers2

0

Use delegateCall in contract B to execute contract A. This executes contract A code with contract B storage.

Not sure what deposit does, it seems more like a withdraw to me.

Nonetheless, contract B needs to have enough allowance from contract A then you can call a delegateCall with transfer from contract B to execute transfer from contact A, and this sends the tokens to msg.sender

This is basically the same as when EOA with enough allowance calls a transfer from the contract to itself, the only difference is that you are using another contract instead.

Read these to understand DelegateCall;

https://solidity-by-example.org/delegatecall/ https://medium.com/coinmonks/delegatecall-calling-another-contract-function-in-solidity-b579f804178c

Edit after a long time:

  1. On the ERC-20 constructor give max allowance to contract B
  2. On deposit do a transferFrom(A, address(this), amount) now B has A amount tokens
  3. Then do transferFrom(address(this), msg.sender, amount) now B uses the amount of tokens that it just got from A and send it to the msg.sender

PD: Its kinda pointless to sent tokens from A to B and then B to sender. You could simple to a transferFrom(A, msg.sender, amount). B must have max allowance on itself to send the second txn

  • This sounds precisely like what I need - only 'tricky' thing is that I'm coding this in Vyper and I dont see support for DelegateCall - any ideas how to acheive this in Vyper ? – Christian Papathanasiou Nov 15 '21 at 10:08
  • This solution won't work, because it requires contract A storage layout to be compatible with contract B (declare data variables in the same order, inherit the same contract in the same order, etc), also contract A storage will never be used, everything will be stored in contract B. – Ismael Nov 16 '21 at 12:13
0

A year older and wiser...

Contract A has an ERC20 balance (and might be an ERC20 contract itself with ERC20(coinA).balanceOf(self) a positive number.

Contract B has no coinA balance but requires a coinA balance.

ContractA has an init method which ERC20(coinA).approve(coinB,MAX_UINT256) this allows contractB to withdraw all of coinA's balance (you could limit this by reducing the MAX_UINT256 to an appropriate value).

ContractB then has a method eg

def pull_coin(_amount: uint256)->uint256: 
   assert msg.sender == self.admin, "only admin"
   ERC20(coinA).transferFrom(contractA,self,_amount)
   ERC20(coinA).transfer(msg.sender,_amount)
   return _amount