Skip to content

Commit

Permalink
feat: complete UndelegateAndTransfer and DepositAndDelegate flows (
Browse files Browse the repository at this point in the history
…#10045)

closes: #10046

## Description
- use `AnyNatAmountShape` for `AmountArg` in `@agoric/orchestration`
- Update  `undelegate` to take `AmountArg` and `ChainAddress`
- Update `undelegate` to accept and `bondDenom`
- Finish `UnelegateAndTransfer` and `DepositAndDelegate` flows in staking-combinations contract

### Security Considerations
n/a

### Scaling Considerations
n/a

### Documentation Considerations
n/a

### Testing Considerations
Updates existing tests and includes tests for new functionality

### Upgrade Considerations
n/a
  • Loading branch information
mergify[bot] authored Sep 10, 2024
2 parents f9057b6 + 2a50005 commit 607ed82
Show file tree
Hide file tree
Showing 25 changed files with 394 additions and 171 deletions.
9 changes: 5 additions & 4 deletions multichain-testing/test/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { RetryOptions } from '../tools/sleep.js';

/**
* Wait 90 seconds to ensure staking rewards are available.
* Wait up to 90 seconds to ensure staking rewards are available.
*
* While we expect staking rewards to be available after a
* single block (~5-12 seconds for most chains), this provides additional
Expand All @@ -18,7 +18,7 @@ export const STAKING_REWARDS_TIMEOUT: RetryOptions = {
};

/**
* Wait 2 minutes to ensure:
* Wait up to 2 minutes to ensure:
* - IBC Transfer from LocalAccount -> ICA Account Completes
* - Delegation from ICA Account (initiated from SwingSet) Completes
* - Delegations are visible via LCD (API Endpoint)
Expand All @@ -32,11 +32,12 @@ export const AUTO_STAKE_IT_DELEGATIONS_TIMEOUT: RetryOptions = {
};

/**
* Wait about 90s to ensure:
* Wait up to 2 minutes to ensure:
* - ICA Account is created
* - ICQ Connection is established (in some instances)
* - Query is executed (sometimes local, sometimes via ICQ)
*/
export const MAKE_ACCOUNT_AND_QUERY_BALANCE_TIMEOUT: RetryOptions = {
maxRetries: 25,
retryIntervalMs: 5000,
maxRetries: 24,
};
24 changes: 11 additions & 13 deletions multichain-testing/test/stake-ica.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,10 @@ const stakeScenario = test.macro(async (t, scenario: StakeIcaScenario) => {
vstorageClient,
retryUntilCondition,
useChain,
deployBuilder,
startContract,
} = t.context;

t.log('bundle and install contract', scenario);
await deployBuilder(scenario.builder);
await retryUntilCondition(
() => vstorageClient.queryData(`published.agoricNames.instance`),
res => scenario.contractName in Object.fromEntries(res),
`${scenario.contractName} instance is available`,
);
await startContract(scenario.contractName, scenario.builder);
const wdUser1 = await provisionSmartWallet(wallets[scenario.wallet], {
BLD: 100n,
IST: 100n,
Expand Down Expand Up @@ -206,7 +200,7 @@ const stakeScenario = test.macro(async (t, scenario: StakeIcaScenario) => {
);
t.log('Balance after claiming rewards:', rewards);

const SHARES = 50;
const TOKENS_TO_UNDELEGATE = 50n;
t.log('Undelegate offer from continuing inv');
const undelegateOfferId = `undelegate-${Date.now()}`;
await doOffer({
Expand All @@ -218,8 +212,11 @@ const stakeScenario = test.macro(async (t, scenario: StakeIcaScenario) => {
invitationArgs: [
[
{
validatorAddress,
shares: String(SHARES),
validator: validatorChainAddress,
amount: {
denom: scenario.denom,
value: TOKENS_TO_UNDELEGATE,
},
},
],
],
Expand All @@ -235,7 +232,7 @@ const stakeScenario = test.macro(async (t, scenario: StakeIcaScenario) => {
t.log('unbonding_responses:', unbonding_responses[0].entries);
t.is(
unbonding_responses[0].entries[0].balance,
String(SHARES),
String(TOKENS_TO_UNDELEGATE),
'undelegating 50 shares in progress',
);

Expand All @@ -250,7 +247,8 @@ const stakeScenario = test.macro(async (t, scenario: StakeIcaScenario) => {
const { balances: rewardsWithUndelegations } = await retryUntilCondition(
() => queryClient.queryBalances(address),
({ balances }) => {
const expectedBalance = Number(currentBalances[0].amount) + SHARES;
const expectedBalance =
Number(currentBalances[0].amount) + Number(TOKENS_TO_UNDELEGATE);
return Number(balances?.[0]?.amount) >= expectedBalance;
},
'claimed rewards available',
Expand Down
8 changes: 5 additions & 3 deletions packages/async-flow/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ export type GuestInterface<T> = {
? (...args: Parameters<T[K]>) => Promise<R>
: T[K] extends HostAsyncFuncWrapper
? GuestOf<T[K]>
: T[K] extends object
? GuestInterface<T[K]>
: T[K];
: T[K] extends (...args: any[]) => infer R
? T[K]
: T[K] extends object
? GuestInterface<T[K]>
: T[K];
};

/**
Expand Down
61 changes: 56 additions & 5 deletions packages/async-flow/test/types.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,73 @@
import { expectType } from 'tsd';
import type { Zone } from '@agoric/base-zone';
import type { Vow, VowTools } from '@agoric/vow';
import type { HostOf, GuestOf } from '../src/types.js';
import type {
HostOf,
GuestOf,
HostInterface,
GuestInterface,
} from '../src/types.js';

const castable: unknown = null;
const vt: VowTools = null as any;

const sumVow = (a: number, b: number) => vt.asVow(() => a + b);

const sumPromise = (a: number, b: number) => Promise.resolve(a + b);

expectType<(p1: number, p2: number) => Promise<number>>(
null as unknown as GuestOf<typeof sumVow>,
castable as GuestOf<typeof sumVow>,
);

expectType<(p1: number, p2: number) => Vow<number>>(
null as unknown as HostOf<typeof sumPromise>,
castable as HostOf<typeof sumPromise>,
);
expectType<(p1: number, p2: number) => Vow<void>>(
// @ts-expect-error incompatible return type
null as unknown as HostOf<typeof sumPromise>,
castable as HostOf<typeof sumPromise>,
);

// Test HostInterface and GuestInterface with an exoClass object
type ExoAPIBase = {
getValue: () => number;
setValue: (value: number) => void;
getCopyData: () => Record<string, number>[];
// TODO include `getRemote() => Guarded<...>`, since durable exos are passable
};
type ExoGuestAPI = ExoAPIBase & {
getValueAsync: () => Promise<number>;
};

type ExoHostAPI = ExoAPIBase & {
getValueAsync: () => Vow<number>;
};

expectType<
ExoAPIBase & {
getValueAsync: () => Vow<number>;
}
>(castable as HostInterface<ExoGuestAPI>);
expectType<
ExoAPIBase & {
getValueAsync: () => Promise<number>;
}
>(castable as GuestInterface<ExoHostAPI>);

// Test HostInterface and GuestInterface with classKit (nested) objects
expectType<{
facet: ExoAPIBase & {
getValueAsync: () => Vow<number>;
};
}>(
castable as HostInterface<{
facet: ExoGuestAPI;
}>,
);
expectType<{
facet: ExoAPIBase & {
getValueAsync: () => Promise<number>;
};
}>(
castable as GuestInterface<{
facet: ExoHostAPI;
}>,
);
4 changes: 2 additions & 2 deletions packages/orchestration/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ See [`src/examples`](src/examples)
| [send-anywhere](/packages/orchestration/src/examples/send-anywhere.contract.js) | Ready 🟢 | Allows sending payments (tokens) over IBC to another chain. | - `LocalOrchestrationAccoun`t<br>- `Vtransfer` (IBC Hooks) |
| [stakeBld](/packages/orchestration/src/examples/stakeBld.contract.js) | Ready 🟢 | Returns a `LocalOrchestrationAccount` that can perform staking actions. | - `LocalOrchestrationAccount` | Ready 🟢 |
| [stakeIca](/packages/orchestration/src/examples/stakeIca.contract.js) | Ready 🟢 | Returns a `CosmosOrchestrationAccount` that can perform staking actions. | - `CosmosOrchestrationAccount` | Ready 🟢 |
| [staking-combinations](/packages/orchestration/src/examples/staking-combinations.contract.js) | Under Construction 🚧 | Combines actions into a single offer flow and demonstrates writing continuing offers. | - `CosmosOrchestrationAccount`<br>- `CombineInvitationMakers` <br>- Continuing Offers |
| [staking-combinations](/packages/orchestration/src/examples/staking-combinations.contract.js) | Ready 🟢 | Combines actions into a single offer flow and demonstrates writing continuing offers. | - `CosmosOrchestrationAccount`<br>- `CombineInvitationMakers` <br>- Continuing Offers |
| [swap](/packages/orchestration/src/examples/swap.contract.js) | Under Construction 🚧 | Demonstrates asset swapping on an external chain. | - `CosmosOrchestrationAccount`<br>- `ChainHub` |
| [unbond](/packages/orchestration/src/examples/unbond.contract.js) | Under Construction 🚧 | Undelegates tokens for an ICA and liquid stakes them. | - `CosmosOrchestrationAccount` |

Expand All @@ -43,4 +43,4 @@ See [`src/examples`](src/examples)
| [stakeBld](/packages/orchestration/src/examples/stakeBld.contract.js) | - Everything*, created before e2e test suite<br> - Consider folding under generic "stake" contract, once [interfaces are the same](https://github.com/Agoric/agoric-sdk/blob/1976c502bcaac2e7d21f42b30447671a61053236/packages/orchestration/src/exos/local-orchestration-account.js#L487)|
| [swap](/packages/orchestration/src/examples/swap.contract.js) | - Everything - contract incomplete ([#8863](https://github.com/Agoric/agoric-sdk/issues/8863)) |
| [unbond](/packages/orchestration/src/examples/unbond.contract.js) | - Everything - contract incomplete ([#9782](https://github.com/Agoric/agoric-sdk/issues/9782)) |
|
| [staking-combinations](/packages/orchestration/src/examples/staking-combinations.contract.js) | Only tested via [unit tests](/packages/orchestration/src/examples/staking-combinations.contract.js) |
2 changes: 1 addition & 1 deletion packages/orchestration/src/cosmos-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ export interface StakingAccountActions {
* @param delegations - the delegation to undelegate
*/
undelegate: (
delegations: Omit<Delegation, 'delegatorAddress'>[],
delegations: { amount: AmountArg; validator: CosmosValidatorAddress }[],
) => Promise<void>;

/**
Expand Down
13 changes: 3 additions & 10 deletions packages/orchestration/src/examples/stakeIca.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export const meta = harden({
chainId: M.string(),
hostConnectionId: M.string(),
controllerConnectionId: M.string(),
bondDenom: M.string(),
icqEnabled: M.boolean(),
},
privateArgsShape: {
Expand All @@ -49,7 +48,6 @@ harden(privateArgsShape);
* chainId: string;
* hostConnectionId: IBCConnectionID;
* controllerConnectionId: IBCConnectionID;
* bondDenom: string;
* icqEnabled: boolean;
* }} StakeIcaTerms
*/
Expand All @@ -66,13 +64,8 @@ harden(privateArgsShape);
* @param {Baggage} baggage
*/
export const start = async (zcf, privateArgs, baggage) => {
const {
chainId,
hostConnectionId,
controllerConnectionId,
bondDenom,
icqEnabled,
} = zcf.getTerms();
const { chainId, hostConnectionId, controllerConnectionId, icqEnabled } =
zcf.getTerms();
const {
agoricNames,
cosmosInterchainService: orchestration,
Expand Down Expand Up @@ -125,7 +118,7 @@ export const start = async (zcf, privateArgs, baggage) => {
chainAddress.value,
);
const holder = makeCosmosOrchestrationAccount(
{ chainAddress, bondDenom, localAddress, remoteAddress },
{ chainAddress, localAddress, remoteAddress },
{
account,
storageNode: accountNode,
Expand Down
Loading

0 comments on commit 607ed82

Please sign in to comment.