Agoric / dapp-orchestration-basics

Agoric Orchestration Sample dApp
2 stars 3 forks source link

Execute IBC Token Transfer from smart contract #11

Open anilhelvaci opened 2 months ago

anilhelvaci commented 2 months ago

Context

In our call with @Jovonni on 2024-07-30, he mentioned this idea.

Problem Definition

Currently, the frontend sends toy tokens (BLD, IST) to Osmosis by executing the IBC transfer inside the browser instead of triggering an offer to have a smart contract to do it. I believe below method is the relevant part for this in frontend code; https://github.com/Agoric/dapp-orchestration-basics/blob/ea55e9ce09d80cee729931ad7bf929fc84c41326/ui/src/components/Orchestration/Orchestration.tsx#L100

In order to demonstrate orchestration features and do this incrementally, we can have a smart contract execute simple IBC transfer as our next step.

dckc commented 2 months ago

OrchestrationAccount.transfer() works on LocalOrchestrationAccount, which you can get from Orchestrator.makeLocalAccount(). I made a quick diagram of relevant parts of orchestration-api.ts and cosmos-api.ts:

classDiagram

    class Orchestrator {
        makeLocalAccount()
        getChain(name)
    }

    Orchestrator --> LocalOrchestrationAccount: makeLocalAccount
    Orchestrator --> CosmosChain: getChain

    OrchestrationAccount <|-- LocalAccountMethods
    LocalAccountMethods <|-- LocalOrchestrationAccount
    OrchestrationAccount <|-- CosmosOrchestrationAccount

    class OrchestrationAccount {
      getAddress()
      transfer(amt, destAddr, opts?)
    }

    class CosmosChain {
      makeAccount()
    }
    CosmosChain --> CosmosOrchestrationAccount: makeAccount

    class LocalAccountMethods {
      deposit(pmt, amtShape?)
    }

    class LocalOrchestrationAccount {
    }

    class CosmosOrchestrationAccount {
      transfer(amt, destAddr, opts?) // TODO #9784
    }

Note that transfer() on CosmosOrchestrationAccount is (currently) TODO:

LocalOrchestrationAccount has deposit(), which is a method that goes beyond the OrchestrationAccount interface. So a contract flow might do...

const localAcct = await orch.makeLocalAccount();
const amt = await localAcct.deposit(pmt);

(TODO: Docs for OrchstrationAccount.deposit() should have this as an @example.)

Then:

const amtTxfrd = await localAcct.transfer(amt, destAddr);

The destAddr might come from...

const destChain = await orch.getChain('osmosis');
const destAcct = await destChain.makeAccount();
const destAddr = destAcct.getAddress();

The pmt might come from the offer handler seat by way of zoeTools.localTransfer:

await localTransfer(seat, localAcct, give);

more docs TODOs:

Jovonni commented 2 months ago

I was getting this error, which made me then use the watch pattern i show here to solve it!

solution using watch pattern: https://github.com/Agoric/dapp-orchestration-basics/pull/10#issuecomment-2282208657

I want to document it here for whoever may run into it:

2024-08-09T22:56:41.439Z SwingSet: ls: v10: Error#1: 0ubld is smaller than 1ubld: insufficient funds

when calling

await localAccount.transfer(amount, remoteAddress);

calling localAccount.deposit(payment); before this works, because the localaccount eventually receives the asset, but localAccount.transfer is where insufficient funds is thrown.

The deposit succeeds:

localAccount.deposit(pmt);

trace

2024-08-09T22:56:41.439Z SwingSet: ls: v10: Logging sent error stack (Error#1)
2024-08-09T22:56:41.439Z SwingSet: ls: v10: Error#1: 0ubld is smaller than 1ubld: insufficient funds
2024-08-09T22:56:41.439Z SwingSet: ls: v10: Error: 0ubld is smaller than 1ubld: insufficient funds
 at apply ()
 at Error (/bundled-source/.../node_modules/ses/src/error/tame-error-constructor.js:60)
 at outbound (.../vats/src/bridge.js:162)
 at outbound (.../vats/src/bridge.js:157)
 at apply ()
 at In "outbound" method of (BridgeManagerKit privateOutbounder) (/bundled-source/.../node_modules/@endo/exo/src/exo-tools.js:171)
 at apply ()
 at localApplyMethod (/bundled-source/.../node_modules/@endo/eventual-send/src/local.js:126)
 at apply ()
 at dispatchToHandler (/bundled-source/.../node_modules/@endo/eventual-send/src/handled-promise.js:156)
 at win (/bundled-source/.../node_modules/@endo/eventual-send/src/handled-promise.js:505)
 at ()

2024-08-09T22:56:41.439Z SwingSet: ls: v10: Error#1 ERROR_NOTE: Sent as error:liveSlots:v10#70001
2024-08-09T22:56:41.456Z SwingSet: ls: v17: Logging sent error stack (RemoteError(error:liveSlots:v10#70001)#1)
2024-08-09T22:56:41.456Z SwingSet: ls: v17: RemoteError(error:liveSlots:v10#70001)#1: 0ubld is smaller than 1ubld: insufficient funds
2024-08-09T22:56:41.456Z SwingSet: ls: v17: Error: 0ubld is smaller than 1ubld: insufficient funds
 at apply ()
 at Error (/bundled-source/.../node_modules/ses/src/error/tame-error-constructor.js:60)
 at makeError (/bundled-source/.../node_modules/ses/src/error/assert.js:351)
 at decodeErrorCommon (/bundled-source/.../node_modules/@endo/marshal/src/marshal.js:309)
 at decodeFromSmallcaps (/bundled-source/.../node_modules/@endo/marshal/src/encodeToSmallcaps.js:437)
 at fromCapData (/bundled-source/.../node_modules/@endo/marshal/src/marshal.js:398)
 at notifyOnePromise (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1219)
 at notify (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1243)
 at dispatchToUserspace (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1512)
 at runWithoutMetering (/bundled-source/.../packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js:59)
 at ()

2024-08-09T22:56:41.456Z SwingSet: ls: v17: RemoteError(error:liveSlots:v10#70001)#1 ERROR_NOTE: Sent as error:liveSlots:v17#70001
2024-08-09T22:56:41.483Z SwingSet: ls: v45: Logging sent error stack (RemoteError(error:liveSlots:v17#70001)#1)
2024-08-09T22:56:41.483Z SwingSet: ls: v45: RemoteError(error:liveSlots:v17#70001)#1: 0ubld is smaller than 1ubld: insufficient funds
2024-08-09T22:56:41.483Z SwingSet: ls: v45: Error: 0ubld is smaller than 1ubld: insufficient funds
 at apply ()
 at Error (/bundled-source/.../node_modules/ses/src/error/tame-error-constructor.js:60)
 at makeError (/bundled-source/.../node_modules/ses/src/error/assert.js:351)
 at decodeErrorCommon (/bundled-source/.../node_modules/@endo/marshal/src/marshal.js:309)
 at decodeFromSmallcaps (/bundled-source/.../node_modules/@endo/marshal/src/encodeToSmallcaps.js:437)
 at fromCapData (/bundled-source/.../node_modules/@endo/marshal/src/marshal.js:398)
 at notifyOnePromise (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1219)
 at notify (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1243)
 at dispatchToUserspace (/bundled-source/.../packages/swingset-liveslots/src/liveslots.js:1512)
 at runWithoutMetering (/bundled-source/.../packages/swingset-xsnap-supervisor/lib/supervisor-subprocess-xsnap.js:59)
 at ()

with my new commit, the deposit, and transfer are happening from inside the contract now

Jovonni commented 2 months ago

looking back into this issue 0ubld is smaller than 1ubld: insufficient funds

dckc commented 2 months ago

do you have more context for the insufficient funds error? What steps led up to it? Do you have a full stack trace?

If you have instructions so that someone else should be able to reproduce it, I suggest opening a new issue and marking this blocked on it

Jovonni commented 2 months ago

opened https://github.com/Agoric/agoric-sdk/issues/9901 to track 0ubld is smaller than 1ubld: insufficient funds