Skip to content

Commit

Permalink
Merge pull request #5453 from BitGo/ben/SC-996-wal-sdk-stake
Browse files Browse the repository at this point in the history
feat(wal): walrus staking transaction builder
  • Loading branch information
benkelcher-bitgo authored Jan 30, 2025
2 parents fa245c4 + 8ffdaf0 commit ee3b648
Show file tree
Hide file tree
Showing 9 changed files with 783 additions and 2 deletions.
22 changes: 21 additions & 1 deletion modules/sdk-coin-sui/src/lib/iface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum SuiTransactionType {
WithdrawStake = 'WithdrawStake',
CustomTx = 'CustomTx',
TokenTransfer = 'TokenTransfer',
WalrusStakeWithPool = 'WalrusStakeWithPool',
}

export interface TransactionExplanation extends BaseTransactionExplanation {
Expand All @@ -30,7 +31,8 @@ export type SuiProgrammableTransaction =
| StakingProgrammableTransaction
| UnstakingProgrammableTransaction
| CustomProgrammableTransaction
| TokenTransferProgrammableTransaction;
| TokenTransferProgrammableTransaction
| WalrusStakingProgrammableTransaction;

export interface TxData {
id?: string;
Expand Down Expand Up @@ -78,6 +80,13 @@ export type TokenTransferProgrammableTransaction =
transactions: TransactionType[];
};

export type WalrusStakingProgrammableTransaction =
| ProgrammableTransaction
| {
inputs: CallArg[] | TransactionBlockInput[];
transactions: TransactionType[];
};

export interface SuiTransaction<T = SuiProgrammableTransaction> {
id?: string;
type: SuiTransactionType;
Expand All @@ -96,6 +105,11 @@ export interface RequestWithdrawStakedSui {
stakedSui: SuiObjectRef;
}

export interface RequestWalrusStakeWithPool {
amount: number;
validatorAddress: SuiAddress;
}

/**
* Method names for the transaction method. Names change based on the type of transaction e.g 'request_add_delegation_mul_coin' for the staking transaction
*/
Expand Down Expand Up @@ -124,6 +138,12 @@ export enum MethodNames {
* @see https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/docs/transfer.md#function-public_transfer
*/
PublicTransfer = '::transfer::public_transfer',
/**
* Walrus stake with pool.
*
* @see https://github.com/MystenLabs/walrus-docs/blob/8ba15d67d7ed0e728077e1600866fddd46fd113b/contracts/walrus/sources/staking.move#L289
*/
WalrusStakeWithPool = 'stake_with_pool',
}

export interface SuiObjectInfo extends SuiObjectRef {
Expand Down
42 changes: 42 additions & 0 deletions modules/sdk-coin-sui/src/lib/resources/walrusConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { SharedObjectRef } from '../mystenlab/types';

export const WALRUS_TESTNET_CONFIG = {
WALRUS_SYSTEM_OBJECT: {
objectId: '0x98ebc47370603fe81d9e15491b2f1443d619d1dab720d586e429ed233e1255c1',
initialSharedVersion: 1,
mutable: true,
} as SharedObjectRef,

WALRUS_STAKING_OBJECT: {
objectId: '0x20266a17b4f1a216727f3eef5772f8d486a9e3b5e319af80a5b75809c035561d',
initialSharedVersion: 334023834,
mutable: true,
} as SharedObjectRef,

WALRUS_PKG_ID: '0x795ddbc26b8cfff2551f45e198b87fc19473f2df50f995376b924ac80e56f88b',
WALRUS_STAKING_MODULE_NAME: 'staking',
WALRUS_STAKE_WITH_POOL_FUN_NAME: 'stake_with_pool',

WAL_PKG_ID: '0x8190b041122eb492bf63cb464476bd68c6b7e570a4079645a8b28732b6197a82',
WAL_COIN_MODULE_NAME: 'wal',
WAL_COIN_NAME: 'WAL',
};

export const WALRUS_PROD_CONFIG = {
...WALRUS_TESTNET_CONFIG,

WALRUS_SYSTEM_OBJECT: {
objectId: 'TODO PROD CONFIG',
initialSharedVersion: 1,
mutable: true,
} as SharedObjectRef,

WALRUS_STAKING_OBJECT: {
objectId: 'TODO PROD CONFIG',
initialSharedVersion: 0, // TODO PROD CONFIG
mutable: true,
} as SharedObjectRef,

WALRUS_PKG_ID: 'TODO PROD CONFIG',
WAL_PKG_ID: 'TODO PROD CONFIG',
};
3 changes: 3 additions & 0 deletions modules/sdk-coin-sui/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ export abstract class Transaction<T> extends BaseTransaction {
if (transactions.length == 1) {
return utils.getSuiTransactionType(transactions[0]);
}
if (transactions.some((tx) => utils.getSuiTransactionType(tx) === SuiTransactionType.WalrusStakeWithPool)) {
return SuiTransactionType.WalrusStakeWithPool;
}
if (transactions.some((tx) => utils.getSuiTransactionType(tx) === SuiTransactionType.AddStake)) {
return SuiTransactionType.AddStake;
}
Expand Down
12 changes: 12 additions & 0 deletions modules/sdk-coin-sui/src/lib/transactionBuilderFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
UnstakingProgrammableTransaction,
SuiProgrammableTransaction,
TokenTransferProgrammableTransaction,
WalrusStakingProgrammableTransaction,
} from './iface';
import { StakingTransaction } from './stakingTransaction';
import { TransferTransaction } from './transferTransaction';
Expand All @@ -23,6 +24,8 @@ import { CustomTransaction } from './customTransaction';
import { CustomTransactionBuilder } from './customTransactionBuilder';
import { TokenTransferBuilder } from './tokenTransferBuilder';
import { TokenTransferTransaction } from './tokenTransferTransaction';
import { WalrusStakingBuilder } from './walrusStakingBuilder';
import { WalrusStakingTransaction } from './walrusStakingTransaction';

export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
constructor(_coinConfig: Readonly<CoinConfig>) {
Expand Down Expand Up @@ -55,6 +58,10 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
const tokenTransferTx = new TokenTransferTransaction(this._coinConfig);
tokenTransferTx.fromRawTransaction(raw);
return this.getTokenTransferBuilder(tokenTransferTx);
case SuiTransactionType.WalrusStakeWithPool:
const walrusStakeTx = new WalrusStakingTransaction(this._coinConfig);
walrusStakeTx.fromRawTransaction(raw);
return this.getWalrusStakingBuilder(walrusStakeTx);
default:
throw new InvalidTransactionError('Invalid transaction');
}
Expand Down Expand Up @@ -88,6 +95,11 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
return this.initializeBuilder(tx, new TokenTransferBuilder(this._coinConfig));
}

/** @inheritdoc */
getWalrusStakingBuilder(tx?: Transaction<WalrusStakingProgrammableTransaction>): WalrusStakingBuilder {
return this.initializeBuilder(tx, new WalrusStakingBuilder(this._coinConfig));
}

/** @inheritdoc */
getWalletInitializationBuilder(): void {
throw new Error('Method not implemented.');
Expand Down
28 changes: 28 additions & 0 deletions modules/sdk-coin-sui/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { BCS, fromB64 } from '@mysten/bcs';
import {
MethodNames,
RequestAddStake,
RequestWalrusStakeWithPool,
StakingProgrammableTransaction,
WalrusStakingProgrammableTransaction,
SuiObjectInfo,
SuiProgrammableTransaction,
SuiTransaction,
Expand Down Expand Up @@ -195,6 +197,7 @@ export class Utils implements BaseUtils {
case SuiTransactionType.TokenTransfer:
return TransactionType.Send;
case SuiTransactionType.AddStake:
case SuiTransactionType.WalrusStakeWithPool:
return TransactionType.StakingAdd;
case SuiTransactionType.WithdrawStake:
return TransactionType.StakingWithdraw;
Expand Down Expand Up @@ -233,6 +236,8 @@ export class Utils implements BaseUtils {
command.target.endsWith(MethodNames.PublicTransfer)
) {
return SuiTransactionType.CustomTx;
} else if (command.target.endsWith(MethodNames.WalrusStakeWithPool)) {
return SuiTransactionType.WalrusStakeWithPool;
} else {
throw new InvalidTransactionError(`unsupported target method ${command.target}`);
}
Expand Down Expand Up @@ -327,6 +332,29 @@ export class Utils implements BaseUtils {
});
}

getWalrusStakeWithPoolRequests(tx: WalrusStakingProgrammableTransaction): RequestWalrusStakeWithPool[] {
const amounts: number[] = [];
const addresses: string[] = [];
tx.transactions.forEach((transaction, i) => {
if (transaction.kind === 'SplitCoins') {
const amountInputIdx = ((transaction as SplitCoinsTransaction).amounts[0] as TransactionBlockInput).index;
amounts.push(utils.getAmount(tx.inputs[amountInputIdx] as TransactionBlockInput));
}
if (transaction.kind === 'MoveCall') {
const validatorAddressInputIdx = ((transaction as MoveCallTransaction).arguments[2] as TransactionBlockInput)
.index;
const validatorAddress = utils.getAddress(tx.inputs[validatorAddressInputIdx] as TransactionBlockInput);
addresses.push(validatorAddress);
}
});
return addresses.map((address, index) => {
return {
validatorAddress: address,
amount: amounts[index],
} as RequestWalrusStakeWithPool;
});
}

getAmount(input: SuiJsonValue | TransactionBlockInput): number {
return isPureArg(input)
? builder.de(BCS.U64, Buffer.from(new Uint16Array(input.Pure)).toString('base64'), 'base64')
Expand Down
Loading

0 comments on commit ee3b648

Please sign in to comment.