Orca Contract Code Walkthrough
This section provides a walkthrough of the Orca contract code, explaining its structure, key components, and functionality. The Orca contract is designed to manage Orchestration accounts and fund them. It interacts with multiple chains and provides functionality for creating accounts and funding them. The code for the contract logic is in two files:
orca.contract.js
The orca.contract.js
file brings in necessary dependencies and types from various Agoric packages. The flows
import contains specific logic for the Orca contract offer handling operations.
import { AmountShape } from '@agoric/ertp';
import { makeTracer } from '@agoric/internal';
import { withOrchestration } from '@agoric/orchestration/src/utils/start-helper.js';
import { ChainInfoShape } from '@agoric/orchestration/src/typeGuards.js';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { M } from '@endo/patterns';
import * as flows from './orca.flows.js';
Type Definitions and Shapes
The following definitions create shapes for validating the structure of amount
and Orchestration powers.
const SingleAmountRecord = M.and(
M.recordOf(M.string(), AmountShape, { numPropertiesLimit: 1 }),
M.not(harden({}))
);
const OrchestrationPowersShape = M.splitRecord({
localchain: M.remotable('localchain'),
orchestrationService: M.remotable('orchestrationService'),
storageNode: M.remotable('storageNode'),
timerService: M.remotable('timerService'),
agoricNames: M.remotable('agoricNames')
});
Main Contract Function
This is the main contract
function that initializes and sets up the contract's functionality.
const contract = async (
zcf,
privateArgs,
zone,
{ orchestrateAll, zoeTools, chainHub }
) => {
// ... (contract logic)
};
Within the contract
function, following actions are performed.
- Chain Registration: Below code section registers chains and their connections with the
chainHub
.
const { chainDetails } = zcf.getTerms();
for (const [name, info] of entries(chainDetails)) {
const { connections = {} } = info;
trace('register', name, {
chainId: info.chainId,
connections: keys(connections)
});
chainHub.registerChain(name, info);
for (const [chainId, connInfo] of entries(connections)) {
chainHub.registerConnection(info.chainId, chainId, connInfo);
}
}
- Creating Account and Funding Functions: These functions are created using the
orchestrateAll
helper, which sets up the necessary flow logic for account creation and funding but the logic is implemented inorca.flows.js
file (discussed below).
const { makeAccount, makeCreateAndFund } = orchestrateAll(flows, {
localTransfer: zoeTools.localTransfer
});
- Public Facet: The public facet provides two methods:
makeAccountInvitation
creates an invitation to make an Orchestration account, andmakeCreateAndFundInvitation
creates an invitation to make an account and fund it.
const publicFacet = zone.exo(
'Orca Public Facet',
M.interface('Orca PF', {
makeAccountInvitation: M.callWhen().returns(InvitationShape),
makeCreateAndFundInvitation: M.callWhen().returns(InvitationShape)
}),
{
makeAccountInvitation() {
return zcf.makeInvitation(makeAccount, 'Make an Orchestration Account');
},
makeCreateAndFundInvitation() {
return zcf.makeInvitation(
makeCreateAndFund,
'Make an Orchestration Account and Fund it',
undefined,
M.splitRecord({ give: SingleAmountRecord })
);
}
}
);
start
Function
The start function is wrapped with withOrchestration
, which provides additional Orchestration setup and tools for the contract.
export const start = withOrchestration(contract);
harden(start);
orca.flows.js
This section provides a walkthrough of the orca.flows.js
file, which contains flow functions for the Orca contract. The orca.flows.js
file defines two main functions:
makeAccount
: Creates an account on a Cosmos chain.makeCreateAndFund
: Creates an account on a Cosmos chain and funds it.
These functions are called by the Zoe vat when a user makes an offer using a corresponding orca contract inivitation.
makeAccount
Function
This function creates an account on a specified Cosmos chain. This function not only creates the account but also returns a continuing offer that allows the user to perform further actions like delegation, rewards withdrawl, and transfers. Here are the parameters of this function:
orch
: An Orchestrator instance parameter represents an instance of theOrchestrator
, a powerful abstraction that manages interactions with the blockchain. It provides methods to interact with different chains, create accounts, and fetch chain information._ctx
: Unused context objectseat
: AZCFSeat
instance. It holds a proposal object corresponding to the current offer.offerArgs
: An object containingchainName
(that identifies the chain the Orchestration account should be created on) anddenom
.
The function validates the offerArgs
to ensure it contains a chainName
, retrieves the specified chain using orch
, creates an account on the chain using chain.makeAccount()
, and returns the account as a continuing offer. Below is the code of makeAccount
after removing some debug information logging code.
mustMatch(offerArgs, M.splitRecord({ chainName: M.string() }));
const { chainName } = offerArgs;
seat.exit();
const chain = await orch.getChain(chainName);
const chainAccount = await chain.makeAccount();
return chainAccount.asContinuingOffer();
Once the account is created, the function returns a continuing offer by calling chainAccount.asContinuingOffer()
. This allows the user to perform further actions on the account, such as delegating tokens or withdrawing rewards, through subsequent offers.
makeCreateAndFund
Function
This function creates an account on a specified Cosmos chain and funds it. It accepts the same set of parameters as make Account
. The function:
- Extracts the amount to be transferred from the seat's proposal, and retrieves both the Agoric chain and the specified target chain.
- Fetches chain info and asset information.
- Creates accounts on both the Agoric chain (local) and the target chain (remote).
- Transfers funds from the seat to the local account.
- Transfers half of the received funds from the local account to the remote account.
- Checks the balance of the remote account, and returns the remote account as a continuing offer.
Below is the code of makeCreateAndFund
after removing some debug information logging code.
const { give } = seat.getProposal();
const [[_kw, amt]] = Object.entries(give);
const [agoric, chain] = await Promise.all([
orch.getChain('agoric'),
orch.getChain(chainName)
]);
const localAccount = await agoric.makeAccount();
const remoteAccount = await chain.makeAccount();
const remoteAddress = await remoteAccount.getAddress();
await localTransfer(seat, localAccount, give);
await localAccount.transfer(
{
denom: 'ubld',
value: amt.value / 2n
},
remoteAddress
);
seat.exit();
return remoteAccount.asContinuingOffer();
As in the previous case, the function returns a continuing offer by calling remoteAccount.asContinuingOffer()
for further actions on the account.
Apart from above mentioned logic, several trace calls are made to log the current state of the function. This is useful for debugging and ensuring that the function's inputs and intermediate states are correct.