-
Notifications
You must be signed in to change notification settings - Fork 307
feat(sdk-core): add wrap() and unwrap() to DefiVault #9569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c6e5e3d
7699502
23c23e5
8d73c5b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /** | ||
| * Wrap native ETH into WETH (and unwrap it back) on staging. | ||
| * | ||
| * Wrap issues a single WETH9 `deposit()` call; unwrap issues `withdraw(uint256)`. | ||
| * The wallet-platform builds the calldata and resolves the WETH9 address from the | ||
| * vault binding — the SDK only forwards vaultId and amount. | ||
| * | ||
| * Set DEFI_WRAP_DIRECTION=unwrap to run the reverse direction. | ||
| * | ||
| * Wrap does not need to be awaited before depositing: the client is free to call | ||
| * depositToVault() without waiting for the wrap to confirm. | ||
| * | ||
| * Usage: | ||
| * STAGING_ACCESS_TOKEN=<token> \ | ||
| * STAGING_WALLET_ID=<walletId> \ | ||
| * STAGING_WALLET_PASSPHRASE=<passphrase> \ | ||
| * DEFI_VAULT_ID=<vaultId> \ | ||
| * DEFI_WRAP_AMOUNT=<amountInBaseUnits> \ | ||
| * DEFI_WRAP_DIRECTION=<wrap|unwrap> \ | ||
| * npx ts-node examples/ts/defi-vault-wrap.ts | ||
| * | ||
| * Copyright 2026, BitGo, Inc. All Rights Reserved. | ||
| */ | ||
| import { BitGo } from 'bitgo'; | ||
|
|
||
| require('dotenv').config({ path: '../../.env' }); | ||
|
|
||
| const config = { | ||
| accessToken: '', | ||
| env: 'staging', | ||
| walletId: '', | ||
| vaultId: 'tbaseeth-weth-test', | ||
| amount: '1000000000000000000', // 1 ETH — 18dp base units, kept as a string | ||
| direction: 'wrap' as 'wrap' | 'unwrap', | ||
| passphrase: '', | ||
| coin: 'tbaseeth', | ||
| otp: '000000', | ||
| }; | ||
|
|
||
| const bitgoTest = new BitGo({ | ||
| env: 'staging', | ||
| }); | ||
|
|
||
| async function main() { | ||
| console.log('Connecting to staging...'); | ||
| bitgoTest.authenticateWithAccessToken({ accessToken: config.accessToken }); | ||
| //await bitgoTest.unlock({ otp: config.otp, duration: 3600 }); | ||
| const wallet = await bitgoTest.coin(config.coin).wallets().get({ id: config.walletId }); | ||
| console.log('Wallet ID :', wallet.id()); | ||
| console.log('Vault ID :', config.vaultId); | ||
| console.log('Direction :', config.direction); | ||
| console.log('Amount :', config.amount, config.direction === 'wrap' ? '(ETH base units)' : '(WETH base units)'); | ||
|
|
||
| const params = { | ||
| vaultId: config.vaultId, | ||
| amount: config.amount, | ||
| ...(config.passphrase ? { walletPassphrase: config.passphrase } : {}), | ||
| }; | ||
|
|
||
| console.log(`\nStarting ${config.direction}...`); | ||
| const result = config.direction === 'wrap' ? await wallet.defi.wrap(params) : await wallet.defi.unwrap(params); | ||
|
|
||
| console.log(`\n${config.direction} submitted:`); | ||
| console.log(' txRequestId :', result.txRequestId); | ||
| // operationId is reserved for milestone M5 and is undefined today. | ||
| console.log('\nFull result:', JSON.stringify(result, null, 2)); | ||
| } | ||
|
|
||
| main().catch((e) => { | ||
| console.error('Error:', e.message); | ||
| if (e.stack) console.error(e.stack); | ||
| process.exit(1); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,18 @@ export const NO_RECIPIENT_TX_TYPES = new Set([ | |
| 'defiApprove', | ||
| 'defiDeposit', | ||
| 'defiWithdraw', | ||
| // Native wrap/unwrap (WETH9 deposit()/withdraw()) — calldata and the WETH9 | ||
| // address are resolved server-side from the vault binding, so no recipients. | ||
| // Registered in BOTH spellings on purpose: this set is matched against | ||
| // txParams.type, which is buildParams.type (camelCase, from wallet.sendMany), | ||
| // AND against intent.intentType (kebab-case, as WP persists it). Signing paths | ||
| // that carry no txParams — notably pendingApproval.approve() → | ||
| // recreateTxRequest() → signTxRequest() with no txParams — only ever see the | ||
| // kebab-case spelling. | ||
| 'wrapNative', | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Could we also add the corresponding calldata check in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will do this in a followup PR.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| 'wrap-native', | ||
| 'unwrapNative', | ||
| 'unwrap-native', | ||
| // ERC-7984 shielding: approve calldata is built server-side from the wrap intent | ||
| 'wrapApprove', | ||
| // Smart contract invocations with no explicit SDK-level recipients | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Could this path fail fast for coins that do not support a wrapped-native DeFi vault?
wallet.defiis available broadly, so unsupported coins currently reach wallet-platform and return an opaque prebuild error. Please reuse/add a shared support guard (or otherwise document why server-side rejection is intentional) and add a clear client-side failure for unsupported coins.