diff --git a/typescript/api-reference/README.md b/typescript/api-reference/README.md index c2ca73552..f18e4f856 100644 --- a/typescript/api-reference/README.md +++ b/typescript/api-reference/README.md @@ -24,6 +24,7 @@ - [ElectrumClient](classes/ElectrumClient.md) - [EthereumAddress](classes/EthereumAddress.md) - [EthereumBridge](classes/EthereumBridge.md) +- [EthereumDepositorProxy](classes/EthereumDepositorProxy.md) - [EthereumTBTCToken](classes/EthereumTBTCToken.md) - [EthereumTBTCVault](classes/EthereumTBTCVault.md) - [EthereumWalletRegistry](classes/EthereumWalletRegistry.md) @@ -52,6 +53,7 @@ - [ChainIdentifier](interfaces/ChainIdentifier.md) - [DepositReceipt](interfaces/DepositReceipt.md) - [DepositRequest](interfaces/DepositRequest.md) +- [DepositorProxy](interfaces/DepositorProxy.md) - [ElectrumCredentials](interfaces/ElectrumCredentials.md) - [EthereumContractConfig](interfaces/EthereumContractConfig.md) - [RedemptionRequest](interfaces/RedemptionRequest.md) @@ -103,6 +105,7 @@ - [ethereumNetworkFromSigner](README.md#ethereumnetworkfromsigner) - [extractBitcoinRawTxVectors](README.md#extractbitcoinrawtxvectors) - [loadEthereumContracts](README.md#loadethereumcontracts) +- [packRevealDepositParameters](README.md#packrevealdepositparameters) - [retryAll](README.md#retryall) - [skipRetryWhenMatched](README.md#skipretrywhenmatched) - [toBitcoinJsLibNetwork](README.md#tobitcoinjslibnetwork) @@ -144,7 +147,7 @@ Represents an event emitted on deposit reveal to the on-chain bridge. #### Defined in -[src/lib/contracts/bridge.ts:283](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L283) +[src/lib/contracts/bridge.ts:293](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L293) ___ @@ -233,7 +236,7 @@ Supported Ethereum networks. #### Defined in -[src/lib/ethereum/index.ts:76](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L76) +[src/lib/ethereum/index.ts:77](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L77) ___ @@ -247,7 +250,7 @@ or a Provider that works only in the read-only mode. #### Defined in -[src/lib/ethereum/index.ts:25](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L25) +[src/lib/ethereum/index.ts:26](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L26) ___ @@ -285,7 +288,7 @@ Represents an event emitted when new wallet is registered on the on-chain bridge #### Defined in -[src/lib/contracts/bridge.ts:445](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L445) +[src/lib/contracts/bridge.ts:455](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L455) ___ @@ -356,7 +359,7 @@ Represents an event emitted on redemption request. #### Defined in -[src/lib/contracts/bridge.ts:334](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L334) +[src/lib/contracts/bridge.ts:344](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L344) ___ @@ -407,7 +410,7 @@ Convenience type aggregating all TBTC contracts handles. #### Defined in -[src/lib/contracts/index.ts:16](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/index.ts#L16) +[src/lib/contracts/index.ts:17](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/index.ts#L17) ## Variables @@ -716,7 +719,7 @@ Throws an error if the address of the signer is not a proper #### Defined in -[src/lib/ethereum/index.ts:63](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L63) +[src/lib/ethereum/index.ts:64](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L64) ___ @@ -740,7 +743,7 @@ Ethereum network. #### Defined in -[src/lib/ethereum/index.ts:32](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L32) +[src/lib/ethereum/index.ts:33](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L33) ___ @@ -796,7 +799,51 @@ Throws an error if the signer's Ethereum network is other than #### Defined in -[src/lib/ethereum/index.ts:87](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L87) +[src/lib/ethereum/index.ts:88](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/index.ts#L88) + +___ + +### packRevealDepositParameters + +▸ **packRevealDepositParameters**(`depositTx`, `depositOutputIndex`, `deposit`, `vault?`): `Object` + +Packs deposit parameters to match the ABI of the revealDeposit and +revealDepositWithExtraData functions of the Ethereum Bridge contract. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `depositTx` | [`BitcoinRawTxVectors`](interfaces/BitcoinRawTxVectors.md) | Deposit transaction data | +| `depositOutputIndex` | `number` | Index of the deposit transaction output that funds the revealed deposit | +| `deposit` | [`DepositReceipt`](interfaces/DepositReceipt.md) | Data of the revealed deposit | +| `vault?` | [`ChainIdentifier`](interfaces/ChainIdentifier.md) | Optional parameter denoting the vault the given deposit should be routed to | + +#### Returns + +`Object` + +Packed parameters. + +| Name | Type | +| :------ | :------ | +| `extraData` | `undefined` \| `string` | +| `fundingTx` | \{ `inputVector`: `string` ; `locktime`: `string` ; `outputVector`: `string` ; `version`: `string` } | +| `fundingTx.inputVector` | `string` | +| `fundingTx.locktime` | `string` | +| `fundingTx.outputVector` | `string` | +| `fundingTx.version` | `string` | +| `reveal` | \{ `blindingFactor`: `string` ; `fundingOutputIndex`: `number` = depositOutputIndex; `refundLocktime`: `string` ; `refundPubKeyHash`: `string` ; `vault`: `string` ; `walletPubKeyHash`: `string` } | +| `reveal.blindingFactor` | `string` | +| `reveal.fundingOutputIndex` | `number` | +| `reveal.refundLocktime` | `string` | +| `reveal.refundPubKeyHash` | `string` | +| `reveal.vault` | `string` | +| `reveal.walletPubKeyHash` | `string` | + +#### Defined in + +[src/lib/ethereum/bridge.ts:691](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L691) ___ @@ -984,4 +1031,4 @@ This function does not validate the depositor's identifier as its #### Defined in -[src/lib/contracts/bridge.ts:228](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L228) +[src/lib/contracts/bridge.ts:233](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L233) diff --git a/typescript/api-reference/classes/Deposit.md b/typescript/api-reference/classes/Deposit.md index b7f06b7fc..3dc82c46f 100644 --- a/typescript/api-reference/classes/Deposit.md +++ b/typescript/api-reference/classes/Deposit.md @@ -14,6 +14,7 @@ This component tries to abstract away that complexity. - [bitcoinClient](Deposit.md#bitcoinclient) - [bitcoinNetwork](Deposit.md#bitcoinnetwork) +- [depositorProxy](Deposit.md#depositorproxy) - [script](Deposit.md#script) - [tbtcContracts](Deposit.md#tbtccontracts) @@ -29,7 +30,7 @@ This component tries to abstract away that complexity. ### constructor -• **new Deposit**(`receipt`, `tbtcContracts`, `bitcoinClient`, `bitcoinNetwork`): [`Deposit`](Deposit.md) +• **new Deposit**(`receipt`, `tbtcContracts`, `bitcoinClient`, `bitcoinNetwork`, `depositorProxy?`): [`Deposit`](Deposit.md) #### Parameters @@ -39,6 +40,7 @@ This component tries to abstract away that complexity. | `tbtcContracts` | [`TBTCContracts`](../README.md#tbtccontracts) | | `bitcoinClient` | [`BitcoinClient`](../interfaces/BitcoinClient.md) | | `bitcoinNetwork` | [`BitcoinNetwork`](../enums/BitcoinNetwork-1.md) | +| `depositorProxy?` | [`DepositorProxy`](../interfaces/DepositorProxy.md) | #### Returns @@ -46,7 +48,7 @@ This component tries to abstract away that complexity. #### Defined in -[src/services/deposits/deposit.ts:42](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L42) +[src/services/deposits/deposit.ts:47](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L47) ## Properties @@ -58,7 +60,7 @@ Bitcoin client handle. #### Defined in -[src/services/deposits/deposit.ts:35](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L35) +[src/services/deposits/deposit.ts:36](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L36) ___ @@ -71,6 +73,18 @@ generated deposit address. #### Defined in +[src/services/deposits/deposit.ts:45](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L45) + +___ + +### depositorProxy + +• `Private` `Optional` `Readonly` **depositorProxy**: [`DepositorProxy`](../interfaces/DepositorProxy.md) + +Optional depositor proxy used to initiate minting. + +#### Defined in + [src/services/deposits/deposit.ts:40](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L40) ___ @@ -83,7 +97,7 @@ Bitcoin script corresponding to this deposit. #### Defined in -[src/services/deposits/deposit.ts:27](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L27) +[src/services/deposits/deposit.ts:28](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L28) ___ @@ -95,7 +109,7 @@ Handle to tBTC contracts. #### Defined in -[src/services/deposits/deposit.ts:31](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L31) +[src/services/deposits/deposit.ts:32](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L32) ## Methods @@ -116,7 +130,7 @@ Specific UTXOs targeting this deposit. Empty array in case #### Defined in -[src/services/deposits/deposit.ts:85](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L85) +[src/services/deposits/deposit.ts:99](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L99) ___ @@ -132,7 +146,7 @@ Bitcoin address corresponding to this deposit. #### Defined in -[src/services/deposits/deposit.ts:74](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L74) +[src/services/deposits/deposit.ts:88](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L88) ___ @@ -148,7 +162,7 @@ Receipt corresponding to this deposit. #### Defined in -[src/services/deposits/deposit.ts:67](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L67) +[src/services/deposits/deposit.ts:81](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L81) ___ @@ -192,13 +206,13 @@ Throws an error if the funding outpoint was already used to #### Defined in -[src/services/deposits/deposit.ts:114](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L114) +[src/services/deposits/deposit.ts:128](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L128) ___ ### fromReceipt -▸ **fromReceipt**(`receipt`, `tbtcContracts`, `bitcoinClient`): `Promise`\<[`Deposit`](Deposit.md)\> +▸ **fromReceipt**(`receipt`, `tbtcContracts`, `bitcoinClient`, `depositorProxy?`): `Promise`\<[`Deposit`](Deposit.md)\> #### Parameters @@ -207,6 +221,7 @@ ___ | `receipt` | [`DepositReceipt`](../interfaces/DepositReceipt.md) | | `tbtcContracts` | [`TBTCContracts`](../README.md#tbtccontracts) | | `bitcoinClient` | [`BitcoinClient`](../interfaces/BitcoinClient.md) | +| `depositorProxy?` | [`DepositorProxy`](../interfaces/DepositorProxy.md) | #### Returns @@ -214,4 +229,4 @@ ___ #### Defined in -[src/services/deposits/deposit.ts:54](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L54) +[src/services/deposits/deposit.ts:61](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L61) diff --git a/typescript/api-reference/classes/DepositScript.md b/typescript/api-reference/classes/DepositScript.md index 43fd05256..fd192a72e 100644 --- a/typescript/api-reference/classes/DepositScript.md +++ b/typescript/api-reference/classes/DepositScript.md @@ -43,7 +43,7 @@ by the target wallet during the deposit sweep process. #### Defined in -[src/services/deposits/deposit.ts:166](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L166) +[src/services/deposits/deposit.ts:189](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L189) ## Properties @@ -56,7 +56,7 @@ and allowing to build a unique deposit script (and address) on Bitcoin chain. #### Defined in -[src/services/deposits/deposit.ts:159](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L159) +[src/services/deposits/deposit.ts:182](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L182) ___ @@ -69,7 +69,7 @@ should be a witness P2WSH one. If false, legacy P2SH will be used instead. #### Defined in -[src/services/deposits/deposit.ts:164](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L164) +[src/services/deposits/deposit.ts:187](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L187) ## Methods @@ -93,7 +93,7 @@ Bitcoin address corresponding to this deposit script. #### Defined in -[src/services/deposits/deposit.ts:228](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L228) +[src/services/deposits/deposit.ts:258](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L258) ___ @@ -109,7 +109,7 @@ Hashed deposit script as Buffer. #### Defined in -[src/services/deposits/deposit.ts:183](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L183) +[src/services/deposits/deposit.ts:206](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L206) ___ @@ -125,7 +125,7 @@ Plain-text deposit script as a hex string. #### Defined in -[src/services/deposits/deposit.ts:195](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L195) +[src/services/deposits/deposit.ts:218](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L218) ___ @@ -146,4 +146,4 @@ ___ #### Defined in -[src/services/deposits/deposit.ts:173](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L173) +[src/services/deposits/deposit.ts:196](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposit.ts#L196) diff --git a/typescript/api-reference/classes/DepositsService.md b/typescript/api-reference/classes/DepositsService.md index 0f9022078..31dad4af5 100644 --- a/typescript/api-reference/classes/DepositsService.md +++ b/typescript/api-reference/classes/DepositsService.md @@ -19,6 +19,7 @@ Service exposing features related to tBTC v2 deposits. - [generateDepositReceipt](DepositsService.md#generatedepositreceipt) - [initiateDeposit](DepositsService.md#initiatedeposit) +- [initiateDepositWithProxy](DepositsService.md#initiatedepositwithproxy) - [setDefaultDepositor](DepositsService.md#setdefaultdepositor) ## Constructors @@ -40,7 +41,7 @@ Service exposing features related to tBTC v2 deposits. #### Defined in -[src/services/deposits/deposits-service.ts:40](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L40) +[src/services/deposits/deposits-service.ts:41](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L41) ## Properties @@ -52,7 +53,7 @@ Bitcoin client handle. #### Defined in -[src/services/deposits/deposits-service.ts:33](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L33) +[src/services/deposits/deposits-service.ts:34](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L34) ___ @@ -65,7 +66,7 @@ initiated by this service. #### Defined in -[src/services/deposits/deposits-service.ts:38](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L38) +[src/services/deposits/deposits-service.ts:39](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L39) ___ @@ -78,7 +79,7 @@ This is 9 month in seconds assuming 1 month = 30 days #### Defined in -[src/services/deposits/deposits-service.ts:25](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L25) +[src/services/deposits/deposits-service.ts:26](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L26) ___ @@ -90,19 +91,21 @@ Handle to tBTC contracts. #### Defined in -[src/services/deposits/deposits-service.ts:29](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L29) +[src/services/deposits/deposits-service.ts:30](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L30) ## Methods ### generateDepositReceipt -▸ **generateDepositReceipt**(`bitcoinRecoveryAddress`): `Promise`\<[`DepositReceipt`](../interfaces/DepositReceipt.md)\> +▸ **generateDepositReceipt**(`bitcoinRecoveryAddress`, `depositor`, `extraData?`): `Promise`\<[`DepositReceipt`](../interfaces/DepositReceipt.md)\> #### Parameters | Name | Type | | :------ | :------ | | `bitcoinRecoveryAddress` | `string` | +| `depositor` | [`ChainIdentifier`](../interfaces/ChainIdentifier.md) | +| `extraData?` | [`Hex`](Hex.md) | #### Returns @@ -110,13 +113,13 @@ Handle to tBTC contracts. #### Defined in -[src/services/deposits/deposits-service.ts:61](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L61) +[src/services/deposits/deposits-service.ts:119](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L119) ___ ### initiateDeposit -▸ **initiateDeposit**(`bitcoinRecoveryAddress`): `Promise`\<[`Deposit`](Deposit.md)\> +▸ **initiateDeposit**(`bitcoinRecoveryAddress`, `extraData?`): `Promise`\<[`Deposit`](Deposit.md)\> Initiates the tBTC v2 deposit process. @@ -125,6 +128,7 @@ Initiates the tBTC v2 deposit process. | Name | Type | Description | | :------ | :------ | :------ | | `bitcoinRecoveryAddress` | `string` | P2PKH or P2WPKH Bitcoin address that can be used for emergency recovery of the deposited funds. | +| `extraData?` | [`Hex`](Hex.md) | Optional 32-byte extra data to be included in the deposit script. Cannot be equal to 32 zero bytes. | #### Returns @@ -138,10 +142,54 @@ Throws an error if one of the following occurs: - The default depositor is not set - There are no active wallet in the Bridge contract - The Bitcoin recovery address is not a valid P2(W)PKH + - The optional extra data is set but is not 32-byte or equals + to 32 zero bytes. + +#### Defined in + +[src/services/deposits/deposits-service.ts:61](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L61) + +___ + +### initiateDepositWithProxy + +▸ **initiateDepositWithProxy**(`bitcoinRecoveryAddress`, `depositorProxy`, `extraData?`): `Promise`\<[`Deposit`](Deposit.md)\> + +Initiates the tBTC v2 deposit process using a depositor proxy. +The depositor proxy initiates minting on behalf of the user (i.e. original +depositor) and receives minted TBTC. This allows the proxy to provide +additional services to the user, such as routing the minted TBTC tokens +to another protocols, in an automated way. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `bitcoinRecoveryAddress` | `string` | P2PKH or P2WPKH Bitcoin address that can be used for emergency recovery of the deposited funds. | +| `depositorProxy` | [`DepositorProxy`](../interfaces/DepositorProxy.md) | Depositor proxy used to initiate the deposit. | +| `extraData?` | [`Hex`](Hex.md) | Optional 32-byte extra data to be included in the deposit script. Cannot be equal to 32 zero bytes. | + +#### Returns + +`Promise`\<[`Deposit`](Deposit.md)\> + +Handle to the initiated deposit process. + +**`See`** + +DepositorProxy + +**`Throws`** + +Throws an error if one of the following occurs: + - There are no active wallet in the Bridge contract + - The Bitcoin recovery address is not a valid P2(W)PKH + - The optional extra data is set but is not 32-byte or equals + to 32 zero bytes. #### Defined in -[src/services/deposits/deposits-service.ts:56](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L56) +[src/services/deposits/deposits-service.ts:100](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L100) ___ @@ -170,4 +218,4 @@ Typically, there is no need to use this method when DepositsService #### Defined in -[src/services/deposits/deposits-service.ts:124](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L124) +[src/services/deposits/deposits-service.ts:197](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/services/deposits/deposits-service.ts#L197) diff --git a/typescript/api-reference/classes/EthereumBridge.md b/typescript/api-reference/classes/EthereumBridge.md index bc84ce9b5..339067f14 100644 --- a/typescript/api-reference/classes/EthereumBridge.md +++ b/typescript/api-reference/classes/EthereumBridge.md @@ -148,7 +148,7 @@ EthersContractHandle.\_totalRetryAttempts #### Defined in -[src/lib/ethereum/bridge.ts:498](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L498) +[src/lib/ethereum/bridge.ts:497](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L497) ___ @@ -177,7 +177,7 @@ Builds the UTXO hash based on the UTXO components. UTXO hash is computed as #### Defined in -[src/lib/ethereum/bridge.ts:622](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L622) +[src/lib/ethereum/bridge.ts:621](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L621) ___ @@ -204,7 +204,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:433](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L433) +[src/lib/ethereum/bridge.ts:432](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L432) ___ @@ -334,7 +334,7 @@ Bridge.getNewWalletRegisteredEvents #### Defined in -[src/lib/ethereum/bridge.ts:534](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L534) +[src/lib/ethereum/bridge.ts:533](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L533) ___ @@ -361,7 +361,7 @@ Bridge.getRedemptionRequestedEvents #### Defined in -[src/lib/ethereum/bridge.ts:639](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L639) +[src/lib/ethereum/bridge.ts:638](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L638) ___ @@ -381,7 +381,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:519](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L519) +[src/lib/ethereum/bridge.ts:518](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L518) ___ @@ -405,7 +405,7 @@ Parsed deposit request. #### Defined in -[src/lib/ethereum/bridge.ts:478](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L478) +[src/lib/ethereum/bridge.ts:477](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L477) ___ @@ -454,7 +454,7 @@ Parsed wallet data. #### Defined in -[src/lib/ethereum/bridge.ts:593](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L593) +[src/lib/ethereum/bridge.ts:592](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L592) ___ @@ -510,7 +510,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:338](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L338) +[src/lib/ethereum/bridge.ts:337](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L337) ___ @@ -568,7 +568,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:272](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L272) +[src/lib/ethereum/bridge.ts:271](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L271) ___ @@ -597,7 +597,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:382](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L382) +[src/lib/ethereum/bridge.ts:381](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L381) ___ @@ -644,7 +644,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:324](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L324) +[src/lib/ethereum/bridge.ts:323](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L323) ___ @@ -664,7 +664,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:559](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L559) +[src/lib/ethereum/bridge.ts:558](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L558) ___ @@ -690,7 +690,7 @@ ___ #### Defined in -[src/lib/ethereum/bridge.ts:576](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L576) +[src/lib/ethereum/bridge.ts:575](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L575) ___ @@ -715,7 +715,7 @@ Deposit key. #### Defined in -[src/lib/ethereum/bridge.ts:459](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L459) +[src/lib/ethereum/bridge.ts:458](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/bridge.ts#L458) ___ diff --git a/typescript/api-reference/classes/EthereumDepositorProxy.md b/typescript/api-reference/classes/EthereumDepositorProxy.md new file mode 100644 index 000000000..334f74953 --- /dev/null +++ b/typescript/api-reference/classes/EthereumDepositorProxy.md @@ -0,0 +1,152 @@ +# Class: EthereumDepositorProxy + +Abstract class representing a depositor proxy contract on Ethereum. +It implements some common logic that is meant to facilitate creation +of concrete depositor proxy handles for Ethereum. + +**`See`** + +for reference. + +## Implements + +- [`DepositorProxy`](../interfaces/DepositorProxy.md) + +## Table of contents + +### Constructors + +- [constructor](EthereumDepositorProxy.md#constructor) + +### Properties + +- [address](EthereumDepositorProxy.md#address) + +### Methods + +- [getChainIdentifier](EthereumDepositorProxy.md#getchainidentifier) +- [packRevealDepositParameters](EthereumDepositorProxy.md#packrevealdepositparameters) +- [revealDeposit](EthereumDepositorProxy.md#revealdeposit) + +## Constructors + +### constructor + +• **new EthereumDepositorProxy**(`address`): [`EthereumDepositorProxy`](EthereumDepositorProxy.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `address` | `string` \| [`EthereumAddress`](EthereumAddress.md) | + +#### Returns + +[`EthereumDepositorProxy`](EthereumDepositorProxy.md) + +#### Defined in + +[src/lib/ethereum/depositor-proxy.ts:16](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/depositor-proxy.ts#L16) + +## Properties + +### address + +• `Protected` `Readonly` **address**: [`EthereumAddress`](EthereumAddress.md) + +#### Defined in + +[src/lib/ethereum/depositor-proxy.ts:14](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/depositor-proxy.ts#L14) + +## Methods + +### getChainIdentifier + +▸ **getChainIdentifier**(): [`ChainIdentifier`](../interfaces/ChainIdentifier.md) + +#### Returns + +[`ChainIdentifier`](../interfaces/ChainIdentifier.md) + +**`See`** + +#### Implementation of + +[DepositorProxy](../interfaces/DepositorProxy.md).[getChainIdentifier](../interfaces/DepositorProxy.md#getchainidentifier) + +#### Defined in + +[src/lib/ethereum/depositor-proxy.ts:28](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/depositor-proxy.ts#L28) + +___ + +### packRevealDepositParameters + +▸ **packRevealDepositParameters**(`depositTx`, `depositOutputIndex`, `deposit`, `vault?`): `Object` + +Packs deposit parameters to match the ABI of the revealDeposit and +revealDepositWithExtraData functions of the Ethereum Bridge contract. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `depositTx` | [`BitcoinRawTxVectors`](../interfaces/BitcoinRawTxVectors.md) | Deposit transaction data | +| `depositOutputIndex` | `number` | Index of the deposit transaction output that funds the revealed deposit | +| `deposit` | [`DepositReceipt`](../interfaces/DepositReceipt.md) | Data of the revealed deposit | +| `vault?` | [`ChainIdentifier`](../interfaces/ChainIdentifier.md) | Optional parameter denoting the vault the given deposit should be routed to | + +#### Returns + +`Object` + +Packed parameters. + +| Name | Type | +| :------ | :------ | +| `extraData` | `undefined` \| `string` | +| `fundingTx` | \{ `inputVector`: `string` ; `locktime`: `string` ; `outputVector`: `string` ; `version`: `string` } | +| `fundingTx.inputVector` | `string` | +| `fundingTx.locktime` | `string` | +| `fundingTx.outputVector` | `string` | +| `fundingTx.version` | `string` | +| `reveal` | \{ `blindingFactor`: `string` ; `fundingOutputIndex`: `number` = depositOutputIndex; `refundLocktime`: `string` ; `refundPubKeyHash`: `string` ; `vault`: `string` ; `walletPubKeyHash`: `string` } | +| `reveal.blindingFactor` | `string` | +| `reveal.fundingOutputIndex` | `number` | +| `reveal.refundLocktime` | `string` | +| `reveal.refundPubKeyHash` | `string` | +| `reveal.vault` | `string` | +| `reveal.walletPubKeyHash` | `string` | + +#### Defined in + +[src/lib/ethereum/depositor-proxy.ts:44](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/depositor-proxy.ts#L44) + +___ + +### revealDeposit + +▸ **revealDeposit**(`depositTx`, `depositOutputIndex`, `deposit`, `vault?`): `Promise`\<[`Hex`](Hex.md)\> + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `depositTx` | [`BitcoinRawTxVectors`](../interfaces/BitcoinRawTxVectors.md) | +| `depositOutputIndex` | `number` | +| `deposit` | [`DepositReceipt`](../interfaces/DepositReceipt.md) | +| `vault?` | [`ChainIdentifier`](../interfaces/ChainIdentifier.md) | + +#### Returns + +`Promise`\<[`Hex`](Hex.md)\> + +**`See`** + +#### Implementation of + +[DepositorProxy](../interfaces/DepositorProxy.md).[revealDeposit](../interfaces/DepositorProxy.md#revealdeposit) + +#### Defined in + +[src/lib/ethereum/depositor-proxy.ts:62](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/ethereum/depositor-proxy.ts#L62) diff --git a/typescript/api-reference/enums/WalletState-1.md b/typescript/api-reference/enums/WalletState-1.md index c48e3da3f..011449fca 100644 --- a/typescript/api-reference/enums/WalletState-1.md +++ b/typescript/api-reference/enums/WalletState-1.md @@ -22,7 +22,7 @@ any action in the Bridge. #### Defined in -[src/lib/contracts/bridge.ts:371](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L371) +[src/lib/contracts/bridge.ts:381](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L381) ___ @@ -36,7 +36,7 @@ and must defend against them. #### Defined in -[src/lib/contracts/bridge.ts:366](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L366) +[src/lib/contracts/bridge.ts:376](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L376) ___ @@ -48,7 +48,7 @@ The wallet can sweep deposits and accept redemption requests. #### Defined in -[src/lib/contracts/bridge.ts:353](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L353) +[src/lib/contracts/bridge.ts:363](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L363) ___ @@ -63,7 +63,7 @@ accepted. #### Defined in -[src/lib/contracts/bridge.ts:360](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L360) +[src/lib/contracts/bridge.ts:370](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L370) ___ @@ -78,7 +78,7 @@ any actions in the Bridge. #### Defined in -[src/lib/contracts/bridge.ts:378](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L378) +[src/lib/contracts/bridge.ts:388](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L388) ___ @@ -90,4 +90,4 @@ The wallet is unknown to the Bridge. #### Defined in -[src/lib/contracts/bridge.ts:349](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L349) +[src/lib/contracts/bridge.ts:359](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L359) diff --git a/typescript/api-reference/interfaces/DepositReceipt.md b/typescript/api-reference/interfaces/DepositReceipt.md index ecd74a655..d74a7e8b0 100644 --- a/typescript/api-reference/interfaces/DepositReceipt.md +++ b/typescript/api-reference/interfaces/DepositReceipt.md @@ -9,6 +9,7 @@ to build a unique deposit address on Bitcoin chain. - [blindingFactor](DepositReceipt.md#blindingfactor) - [depositor](DepositReceipt.md#depositor) +- [extraData](DepositReceipt.md#extradata) - [refundLocktime](DepositReceipt.md#refundlocktime) - [refundPublicKeyHash](DepositReceipt.md#refundpublickeyhash) - [walletPublicKeyHash](DepositReceipt.md#walletpublickeyhash) @@ -40,6 +41,18 @@ Depositor's chain identifier. ___ +### extraData + +• `Optional` **extraData**: [`Hex`](../classes/Hex.md) + +Optional 32-byte extra data. + +#### Defined in + +[src/lib/contracts/bridge.ts:223](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L223) + +___ + ### refundLocktime • **refundLocktime**: [`Hex`](../classes/Hex.md) diff --git a/typescript/api-reference/interfaces/DepositRequest.md b/typescript/api-reference/interfaces/DepositRequest.md index c9cda320b..a204ba5b2 100644 --- a/typescript/api-reference/interfaces/DepositRequest.md +++ b/typescript/api-reference/interfaces/DepositRequest.md @@ -23,7 +23,7 @@ Deposit amount in satoshis. #### Defined in -[src/lib/contracts/bridge.ts:257](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L257) +[src/lib/contracts/bridge.ts:267](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L267) ___ @@ -35,7 +35,7 @@ Depositor's chain identifier. #### Defined in -[src/lib/contracts/bridge.ts:252](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L252) +[src/lib/contracts/bridge.ts:262](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L262) ___ @@ -47,7 +47,7 @@ UNIX timestamp the deposit was revealed at. #### Defined in -[src/lib/contracts/bridge.ts:267](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L267) +[src/lib/contracts/bridge.ts:277](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L277) ___ @@ -60,7 +60,7 @@ should have zero as value. #### Defined in -[src/lib/contracts/bridge.ts:272](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L272) +[src/lib/contracts/bridge.ts:282](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L282) ___ @@ -73,7 +73,7 @@ Denominated in satoshi. #### Defined in -[src/lib/contracts/bridge.ts:277](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L277) +[src/lib/contracts/bridge.ts:287](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L287) ___ @@ -85,4 +85,4 @@ Optional identifier of the vault the deposit should be routed in. #### Defined in -[src/lib/contracts/bridge.ts:262](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L262) +[src/lib/contracts/bridge.ts:272](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L272) diff --git a/typescript/api-reference/interfaces/DepositorProxy.md b/typescript/api-reference/interfaces/DepositorProxy.md new file mode 100644 index 000000000..9eba5ed1c --- /dev/null +++ b/typescript/api-reference/interfaces/DepositorProxy.md @@ -0,0 +1,63 @@ +# Interface: DepositorProxy + +Interface representing a depositor proxy contract. A depositor proxy +is used to reveal deposits to the Bridge, on behalf of the user +(i.e. original depositor). It receives minted TBTC tokens and can provide +additional services to the user, such as routing the minted TBTC tokens to +another protocols, in an automated way. Depositor proxy is responsible for +attributing the deposit and minted TBTC tokens to the user (e.g. using the +optional 32-byte extra data field of the deposit script). + +## Implemented by + +- [`EthereumDepositorProxy`](../classes/EthereumDepositorProxy.md) + +## Table of contents + +### Methods + +- [getChainIdentifier](DepositorProxy.md#getchainidentifier) +- [revealDeposit](DepositorProxy.md#revealdeposit) + +## Methods + +### getChainIdentifier + +▸ **getChainIdentifier**(): [`ChainIdentifier`](ChainIdentifier.md) + +Gets the chain-specific identifier of this contract. + +#### Returns + +[`ChainIdentifier`](ChainIdentifier.md) + +#### Defined in + +[src/lib/contracts/depositor-proxy.ts:19](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/depositor-proxy.ts#L19) + +___ + +### revealDeposit + +▸ **revealDeposit**(`depositTx`, `depositOutputIndex`, `deposit`, `vault?`): `Promise`\<[`Hex`](../classes/Hex.md)\> + +Reveals a given deposit to the on-chain Bridge contract. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `depositTx` | [`BitcoinRawTxVectors`](BitcoinRawTxVectors.md) | Deposit transaction data | +| `depositOutputIndex` | `number` | Index of the deposit transaction output that funds the revealed deposit | +| `deposit` | [`DepositReceipt`](DepositReceipt.md) | Data of the revealed deposit | +| `vault?` | [`ChainIdentifier`](ChainIdentifier.md) | Optional parameter denoting the vault the given deposit should be routed to | + +#### Returns + +`Promise`\<[`Hex`](../classes/Hex.md)\> + +Transaction hash of the reveal deposit transaction. + +#### Defined in + +[src/lib/contracts/depositor-proxy.ts:31](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/depositor-proxy.ts#L31) diff --git a/typescript/api-reference/interfaces/RedemptionRequest.md b/typescript/api-reference/interfaces/RedemptionRequest.md index 864b07f4c..b1ce2635e 100644 --- a/typescript/api-reference/interfaces/RedemptionRequest.md +++ b/typescript/api-reference/interfaces/RedemptionRequest.md @@ -23,7 +23,7 @@ On-chain identifier of the redeemer. #### Defined in -[src/lib/contracts/bridge.ts:296](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L296) +[src/lib/contracts/bridge.ts:306](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L306) ___ @@ -36,7 +36,7 @@ prepended with length. #### Defined in -[src/lib/contracts/bridge.ts:302](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L302) +[src/lib/contracts/bridge.ts:312](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L312) ___ @@ -50,7 +50,7 @@ by the sum of the fee share and the treasury fee for this particular output. #### Defined in -[src/lib/contracts/bridge.ts:309](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L309) +[src/lib/contracts/bridge.ts:319](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L319) ___ @@ -62,7 +62,7 @@ UNIX timestamp the request was created at. #### Defined in -[src/lib/contracts/bridge.ts:328](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L328) +[src/lib/contracts/bridge.ts:338](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L338) ___ @@ -77,7 +77,7 @@ on-chain contract at the time the redemption request was made. #### Defined in -[src/lib/contracts/bridge.ts:317](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L317) +[src/lib/contracts/bridge.ts:327](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L327) ___ @@ -90,4 +90,4 @@ redemption's `requestedAmount` to pay the transaction network fee. #### Defined in -[src/lib/contracts/bridge.ts:323](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L323) +[src/lib/contracts/bridge.ts:333](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L333) diff --git a/typescript/api-reference/interfaces/Wallet.md b/typescript/api-reference/interfaces/Wallet.md index 6387c08e3..05574d700 100644 --- a/typescript/api-reference/interfaces/Wallet.md +++ b/typescript/api-reference/interfaces/Wallet.md @@ -27,7 +27,7 @@ UNIX timestamp indicating the moment the wallet's closing period started. #### Defined in -[src/lib/contracts/bridge.ts:427](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L427) +[src/lib/contracts/bridge.ts:437](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L437) ___ @@ -39,7 +39,7 @@ UNIX timestamp the wallet was created at. #### Defined in -[src/lib/contracts/bridge.ts:418](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L418) +[src/lib/contracts/bridge.ts:428](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L428) ___ @@ -51,7 +51,7 @@ Identifier of a ECDSA Wallet registered in the ECDSA Wallet Registry. #### Defined in -[src/lib/contracts/bridge.ts:402](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L402) +[src/lib/contracts/bridge.ts:412](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L412) ___ @@ -63,7 +63,7 @@ Latest wallet's main UTXO hash. #### Defined in -[src/lib/contracts/bridge.ts:410](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L410) +[src/lib/contracts/bridge.ts:420](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L420) ___ @@ -76,7 +76,7 @@ funds. #### Defined in -[src/lib/contracts/bridge.ts:423](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L423) +[src/lib/contracts/bridge.ts:433](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L433) ___ @@ -88,7 +88,7 @@ Moving funds target wallet commitment submitted by the wallet. #### Defined in -[src/lib/contracts/bridge.ts:439](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L439) +[src/lib/contracts/bridge.ts:449](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L449) ___ @@ -100,7 +100,7 @@ Total count of pending moved funds sweep requests targeting this wallet. #### Defined in -[src/lib/contracts/bridge.ts:431](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L431) +[src/lib/contracts/bridge.ts:441](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L441) ___ @@ -112,7 +112,7 @@ The total redeemable value of pending redemption requests targeting that wallet. #### Defined in -[src/lib/contracts/bridge.ts:414](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L414) +[src/lib/contracts/bridge.ts:424](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L424) ___ @@ -124,7 +124,7 @@ Current state of the wallet. #### Defined in -[src/lib/contracts/bridge.ts:435](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L435) +[src/lib/contracts/bridge.ts:445](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L445) ___ @@ -136,4 +136,4 @@ Compressed public key of the ECDSA Wallet. #### Defined in -[src/lib/contracts/bridge.ts:406](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L406) +[src/lib/contracts/bridge.ts:416](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L416) diff --git a/typescript/api-reference/modules/WalletState.md b/typescript/api-reference/modules/WalletState.md index 3d2991d40..79879efef 100644 --- a/typescript/api-reference/modules/WalletState.md +++ b/typescript/api-reference/modules/WalletState.md @@ -24,4 +24,4 @@ #### Defined in -[src/lib/contracts/bridge.ts:384](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L384) +[src/lib/contracts/bridge.ts:394](https://github.com/keep-network/tbtc-v2/blob/main/typescript/src/lib/contracts/bridge.ts#L394) diff --git a/typescript/src/lib/contracts/bridge.ts b/typescript/src/lib/contracts/bridge.ts index ebf9f1bec..cb6fe0af7 100644 --- a/typescript/src/lib/contracts/bridge.ts +++ b/typescript/src/lib/contracts/bridge.ts @@ -216,6 +216,11 @@ export interface DepositReceipt { * A 4-byte little-endian refund locktime. */ refundLocktime: Hex + + /** + * Optional 32-byte extra data. + */ + extraData?: Hex } // eslint-disable-next-line valid-jsdoc @@ -240,6 +245,11 @@ export function validateDepositReceipt(receipt: DepositReceipt) { if (receipt.refundLocktime.toString().length != 8) { throw new Error("Refund locktime must be a 4-byte number") } + + const extraData = receipt.extraData + if (extraData && extraData.toString().length != 64) { + throw new Error("Extra data must be a 32-byte number") + } } /** diff --git a/typescript/src/lib/contracts/depositor-proxy.ts b/typescript/src/lib/contracts/depositor-proxy.ts new file mode 100644 index 000000000..344e65529 --- /dev/null +++ b/typescript/src/lib/contracts/depositor-proxy.ts @@ -0,0 +1,37 @@ +import { ChainIdentifier } from "./chain-identifier" +import { Hex } from "../utils" +import { BitcoinRawTxVectors } from "../bitcoin" +import { DepositReceipt } from "./bridge" + +/** + * Interface representing a depositor proxy contract. A depositor proxy + * is used to reveal deposits to the Bridge, on behalf of the user + * (i.e. original depositor). It receives minted TBTC tokens and can provide + * additional services to the user, such as routing the minted TBTC tokens to + * another protocols, in an automated way. Depositor proxy is responsible for + * attributing the deposit and minted TBTC tokens to the user (e.g. using the + * optional 32-byte extra data field of the deposit script). + */ +export interface DepositorProxy { + /** + * Gets the chain-specific identifier of this contract. + */ + getChainIdentifier(): ChainIdentifier + + /** + * Reveals a given deposit to the on-chain Bridge contract. + * @param depositTx - Deposit transaction data + * @param depositOutputIndex - Index of the deposit transaction output that + * funds the revealed deposit + * @param deposit - Data of the revealed deposit + * @param vault - Optional parameter denoting the vault the given deposit + * should be routed to + * @returns Transaction hash of the reveal deposit transaction. + */ + revealDeposit( + depositTx: BitcoinRawTxVectors, + depositOutputIndex: number, + deposit: DepositReceipt, + vault?: ChainIdentifier + ): Promise +} diff --git a/typescript/src/lib/contracts/index.ts b/typescript/src/lib/contracts/index.ts index 387367de9..818c4046b 100644 --- a/typescript/src/lib/contracts/index.ts +++ b/typescript/src/lib/contracts/index.ts @@ -1,6 +1,7 @@ export * from "./bridge" export * from "./chain-event" export * from "./chain-identifier" +export * from "./depositor-proxy" export * from "./tbtc-token" export * from "./tbtc-vault" export * from "./wallet-registry" diff --git a/typescript/src/lib/ethereum/bridge.ts b/typescript/src/lib/ethereum/bridge.ts index 9d1bcb7c9..fc3524aa0 100644 --- a/typescript/src/lib/ethereum/bridge.ts +++ b/typescript/src/lib/ethereum/bridge.ts @@ -237,25 +237,24 @@ export class EthereumBridge deposit: DepositReceipt, vault?: ChainIdentifier ): Promise { - const depositTxParam = { - version: depositTx.version.toPrefixedString(), - inputVector: depositTx.inputs.toPrefixedString(), - outputVector: depositTx.outputs.toPrefixedString(), - locktime: depositTx.locktime.toPrefixedString(), - } - - const revealParam = { - fundingOutputIndex: depositOutputIndex, - blindingFactor: deposit.blindingFactor.toPrefixedString(), - walletPubKeyHash: deposit.walletPublicKeyHash.toPrefixedString(), - refundPubKeyHash: deposit.refundPublicKeyHash.toPrefixedString(), - refundLocktime: deposit.refundLocktime.toPrefixedString(), - vault: vault ? `0x${vault.identifierHex}` : constants.AddressZero, - } + const { fundingTx, reveal, extraData } = packRevealDepositParameters( + depositTx, + depositOutputIndex, + deposit, + vault + ) const tx = await EthersTransactionUtils.sendWithRetry( async () => { - return await this._instance.revealDeposit(depositTxParam, revealParam) + if (typeof extraData !== "undefined") { + return await this._instance.revealDepositWithExtraData( + fundingTx, + reveal, + extraData + ) + } + + return await this._instance.revealDeposit(fundingTx, reveal) }, this._totalRetryAttempts, undefined, @@ -677,3 +676,45 @@ export class EthereumBridge }) } } + +/** + * Packs deposit parameters to match the ABI of the revealDeposit and + * revealDepositWithExtraData functions of the Ethereum Bridge contract. + * @param depositTx - Deposit transaction data + * @param depositOutputIndex - Index of the deposit transaction output that + * funds the revealed deposit + * @param deposit - Data of the revealed deposit + * @param vault - Optional parameter denoting the vault the given deposit + * should be routed to + * @returns Packed parameters. + */ +export function packRevealDepositParameters( + depositTx: BitcoinRawTxVectors, + depositOutputIndex: number, + deposit: DepositReceipt, + vault?: ChainIdentifier +) { + const fundingTx = { + version: depositTx.version.toPrefixedString(), + inputVector: depositTx.inputs.toPrefixedString(), + outputVector: depositTx.outputs.toPrefixedString(), + locktime: depositTx.locktime.toPrefixedString(), + } + + const reveal = { + fundingOutputIndex: depositOutputIndex, + blindingFactor: deposit.blindingFactor.toPrefixedString(), + walletPubKeyHash: deposit.walletPublicKeyHash.toPrefixedString(), + refundPubKeyHash: deposit.refundPublicKeyHash.toPrefixedString(), + refundLocktime: deposit.refundLocktime.toPrefixedString(), + vault: vault ? `0x${vault.identifierHex}` : constants.AddressZero, + } + + const extraData: string | undefined = deposit.extraData?.toPrefixedString() + + return { + fundingTx, + reveal, + extraData, + } +} diff --git a/typescript/src/lib/ethereum/depositor-proxy.ts b/typescript/src/lib/ethereum/depositor-proxy.ts new file mode 100644 index 000000000..d099f9666 --- /dev/null +++ b/typescript/src/lib/ethereum/depositor-proxy.ts @@ -0,0 +1,68 @@ +import { ChainIdentifier, DepositorProxy, DepositReceipt } from "../contracts" +import { BitcoinRawTxVectors } from "../bitcoin" +import { Hex } from "../utils" +import { EthereumAddress } from "./address" +import { packRevealDepositParameters } from "./bridge" + +/** + * Abstract class representing a depositor proxy contract on Ethereum. + * It implements some common logic that is meant to facilitate creation + * of concrete depositor proxy handles for Ethereum. + * @see {DepositorProxy} for reference. + */ +export abstract class EthereumDepositorProxy implements DepositorProxy { + protected readonly address: EthereumAddress + + protected constructor(address: string | EthereumAddress) { + if (typeof address === "string") { + this.address = EthereumAddress.from(address) + } else { + this.address = address + } + } + + // eslint-disable-next-line valid-jsdoc + /** + * @see {DepositorProxy#getChainIdentifier} + */ + getChainIdentifier(): ChainIdentifier { + return this.address + } + + /** + * Packs deposit parameters to match the ABI of the revealDeposit and + * revealDepositWithExtraData functions of the Ethereum Bridge contract. + * @param depositTx - Deposit transaction data + * @param depositOutputIndex - Index of the deposit transaction output that + * funds the revealed deposit + * @param deposit - Data of the revealed deposit + * @param vault - Optional parameter denoting the vault the given deposit + * should be routed to + * @returns Packed parameters. + * @protected + */ + protected packRevealDepositParameters( + depositTx: BitcoinRawTxVectors, + depositOutputIndex: number, + deposit: DepositReceipt, + vault?: ChainIdentifier + ) { + return packRevealDepositParameters( + depositTx, + depositOutputIndex, + deposit, + vault + ) + } + + // eslint-disable-next-line valid-jsdoc + /** + * @see {DepositorProxy#revealDeposit} + */ + abstract revealDeposit( + depositTx: BitcoinRawTxVectors, + depositOutputIndex: number, + deposit: DepositReceipt, + vault?: ChainIdentifier + ): Promise +} diff --git a/typescript/src/lib/ethereum/index.ts b/typescript/src/lib/ethereum/index.ts index f8e9f40be..5524f8993 100644 --- a/typescript/src/lib/ethereum/index.ts +++ b/typescript/src/lib/ethereum/index.ts @@ -8,6 +8,7 @@ import { EthereumAddress } from "./address" export * from "./address" export * from "./bridge" +export * from "./depositor-proxy" export * from "./tbtc-token" export * from "./tbtc-vault" export * from "./wallet-registry" diff --git a/typescript/src/services/deposits/deposit.ts b/typescript/src/services/deposits/deposit.ts index 58fa4c906..d544d7c94 100644 --- a/typescript/src/services/deposits/deposit.ts +++ b/typescript/src/services/deposits/deposit.ts @@ -1,4 +1,5 @@ import { + DepositorProxy, DepositReceipt, TBTCContracts, validateDepositReceipt, @@ -33,6 +34,10 @@ export class Deposit { * Bitcoin client handle. */ private readonly bitcoinClient: BitcoinClient + /** + * Optional depositor proxy used to initiate minting. + */ + private readonly depositorProxy?: DepositorProxy /** * Bitcoin network the deposit is relevant for. Has an impact on the * generated deposit address. @@ -43,22 +48,31 @@ export class Deposit { receipt: DepositReceipt, tbtcContracts: TBTCContracts, bitcoinClient: BitcoinClient, - bitcoinNetwork: BitcoinNetwork + bitcoinNetwork: BitcoinNetwork, + depositorProxy?: DepositorProxy ) { this.script = DepositScript.fromReceipt(receipt) this.tbtcContracts = tbtcContracts this.bitcoinClient = bitcoinClient this.bitcoinNetwork = bitcoinNetwork + this.depositorProxy = depositorProxy } static async fromReceipt( receipt: DepositReceipt, tbtcContracts: TBTCContracts, - bitcoinClient: BitcoinClient + bitcoinClient: BitcoinClient, + depositorProxy?: DepositorProxy ): Promise { const bitcoinNetwork = await bitcoinClient.getNetwork() - return new Deposit(receipt, tbtcContracts, bitcoinClient, bitcoinNetwork) + return new Deposit( + receipt, + tbtcContracts, + bitcoinClient, + bitcoinNetwork, + depositorProxy + ) } /** @@ -135,6 +149,15 @@ export class Deposit { const { bridge, tbtcVault } = this.tbtcContracts + if (typeof this.depositorProxy !== "undefined") { + return this.depositorProxy.revealDeposit( + depositFundingTx, + outputIndex, + this.getReceipt(), + tbtcVault.getChainIdentifier() + ) + } + return bridge.revealDeposit( depositFundingTx, outputIndex, @@ -198,6 +221,13 @@ export class DepositScript { // All HEXes pushed to the script must be un-prefixed chunks.push(Buffer.from(this.receipt.depositor.identifierHex, "hex")) chunks.push(opcodes.OP_DROP) + + const extraData = this.receipt.extraData + if (typeof extraData !== "undefined") { + chunks.push(extraData.toBuffer()) + chunks.push(opcodes.OP_DROP) + } + chunks.push(this.receipt.blindingFactor.toBuffer()) chunks.push(opcodes.OP_DROP) chunks.push(opcodes.OP_DUP) diff --git a/typescript/src/services/deposits/deposits-service.ts b/typescript/src/services/deposits/deposits-service.ts index eec881269..974708941 100644 --- a/typescript/src/services/deposits/deposits-service.ts +++ b/typescript/src/services/deposits/deposits-service.ts @@ -1,5 +1,6 @@ import { ChainIdentifier, + DepositorProxy, DepositReceipt, TBTCContracts, } from "../../lib/contracts" @@ -47,26 +48,79 @@ export class DepositsService { * @param bitcoinRecoveryAddress P2PKH or P2WPKH Bitcoin address that can * be used for emergency recovery of the * deposited funds. + * @param extraData Optional 32-byte extra data to be included in the + * deposit script. Cannot be equal to 32 zero bytes. * @returns Handle to the initiated deposit process. * @throws Throws an error if one of the following occurs: * - The default depositor is not set * - There are no active wallet in the Bridge contract * - The Bitcoin recovery address is not a valid P2(W)PKH + * - The optional extra data is set but is not 32-byte or equals + * to 32 zero bytes. */ - async initiateDeposit(bitcoinRecoveryAddress: string): Promise { - const receipt = await this.generateDepositReceipt(bitcoinRecoveryAddress) - return Deposit.fromReceipt(receipt, this.tbtcContracts, this.bitcoinClient) - } - - private async generateDepositReceipt( - bitcoinRecoveryAddress: string - ): Promise { + async initiateDeposit( + bitcoinRecoveryAddress: string, + extraData?: Hex + ): Promise { if (this.defaultDepositor === undefined) { throw new Error( "Default depositor is not set; use setDefaultDepositor first" ) } + const receipt = await this.generateDepositReceipt( + bitcoinRecoveryAddress, + this.defaultDepositor, + extraData + ) + + return Deposit.fromReceipt(receipt, this.tbtcContracts, this.bitcoinClient) + } + + /** + * Initiates the tBTC v2 deposit process using a depositor proxy. + * The depositor proxy initiates minting on behalf of the user (i.e. original + * depositor) and receives minted TBTC. This allows the proxy to provide + * additional services to the user, such as routing the minted TBTC tokens + * to another protocols, in an automated way. + * @see DepositorProxy + * @param bitcoinRecoveryAddress P2PKH or P2WPKH Bitcoin address that can + * be used for emergency recovery of the + * deposited funds. + * @param depositorProxy Depositor proxy used to initiate the deposit. + * @param extraData Optional 32-byte extra data to be included in the + * deposit script. Cannot be equal to 32 zero bytes. + * @returns Handle to the initiated deposit process. + * @throws Throws an error if one of the following occurs: + * - There are no active wallet in the Bridge contract + * - The Bitcoin recovery address is not a valid P2(W)PKH + * - The optional extra data is set but is not 32-byte or equals + * to 32 zero bytes. + */ + async initiateDepositWithProxy( + bitcoinRecoveryAddress: string, + depositorProxy: DepositorProxy, + extraData?: Hex + ): Promise { + const receipt = await this.generateDepositReceipt( + bitcoinRecoveryAddress, + depositorProxy.getChainIdentifier(), + extraData + ) + + return Deposit.fromReceipt( + receipt, + this.tbtcContracts, + this.bitcoinClient, + depositorProxy + ) + } + + private async generateDepositReceipt( + bitcoinRecoveryAddress: string, + depositor: ChainIdentifier, + extraData?: Hex + ): Promise { const blindingFactor = Hex.from(crypto.randomBytes(8)) const walletPublicKey = @@ -103,12 +157,31 @@ export class DepositsService { this.depositRefundLocktimeDuration ) + // If optional extra data is provided, check if it is valid and fail + // fast if not. + if (typeof extraData !== "undefined") { + // Check if extra data vector has a correct length of 32 bytes. + if (extraData.toString().length != 64) { + throw new Error("Extra data is not 32-byte") + } + // Check if extra data vector is non-zero. This is important because a + // deposit with defined extra data is handled via a special flow of + // the Bridge and this vector is expected to be non-zero. + if ( + extraData.toPrefixedString() === + "0x0000000000000000000000000000000000000000000000000000000000000000" + ) { + throw new Error("Extra data contains only zero bytes") + } + } + return { - depositor: this.defaultDepositor, + depositor, blindingFactor, walletPublicKeyHash, refundPublicKeyHash, refundLocktime, + extraData, } } diff --git a/typescript/test/lib/ethereum.test.ts b/typescript/test/lib/ethereum.test.ts index 1b89d253f..b4fb9650a 100644 --- a/typescript/test/lib/ethereum.test.ts +++ b/typescript/test/lib/ethereum.test.ts @@ -139,52 +139,112 @@ describe("Ethereum", () => { }) describe("revealDeposit", () => { - beforeEach(async () => { - await bridgeContract.mock.revealDeposit.returns() + context("when deposit does not have optional extra data", () => { + beforeEach(async () => { + await bridgeContract.mock.revealDeposit.returns() + + await bridgeHandle.revealDeposit( + // Just short byte strings for clarity. + { + version: Hex.from("00000000"), + inputs: Hex.from("11111111"), + outputs: Hex.from("22222222"), + locktime: Hex.from("33333333"), + }, + 2, + { + depositor: EthereumAddress.from( + "934b98637ca318a4d6e7ca6ffd1690b8e77df637" + ), + walletPublicKeyHash: Hex.from( + "8db50eb52063ea9d98b3eac91489a90f738986f6" + ), + refundPublicKeyHash: Hex.from( + "28e081f285138ccbe389c1eb8985716230129f89" + ), + blindingFactor: Hex.from("f9f0c90d00039523"), + refundLocktime: Hex.from("60bcea61"), + }, + EthereumAddress.from("82883a4c7a8dd73ef165deb402d432613615ced4") + ) + }) - await bridgeHandle.revealDeposit( - // Just short byte strings for clarity. - { - version: Hex.from("00000000"), - inputs: Hex.from("11111111"), - outputs: Hex.from("22222222"), - locktime: Hex.from("33333333"), - }, - 2, - { - depositor: EthereumAddress.from( - "934b98637ca318a4d6e7ca6ffd1690b8e77df637" - ), - walletPublicKeyHash: Hex.from( - "8db50eb52063ea9d98b3eac91489a90f738986f6" - ), - refundPublicKeyHash: Hex.from( - "28e081f285138ccbe389c1eb8985716230129f89" - ), - blindingFactor: Hex.from("f9f0c90d00039523"), - refundLocktime: Hex.from("60bcea61"), - }, - EthereumAddress.from("82883a4c7a8dd73ef165deb402d432613615ced4") - ) + it("should reveal the deposit", async () => { + assertContractCalledWith(bridgeContract, "revealDeposit", [ + { + version: "0x00000000", + inputVector: "0x11111111", + outputVector: "0x22222222", + locktime: "0x33333333", + }, + { + fundingOutputIndex: 2, + blindingFactor: "0xf9f0c90d00039523", + walletPubKeyHash: "0x8db50eb52063ea9d98b3eac91489a90f738986f6", + refundPubKeyHash: "0x28e081f285138ccbe389c1eb8985716230129f89", + refundLocktime: "0x60bcea61", + vault: "0x82883a4c7a8dd73ef165deb402d432613615ced4", + }, + ]) + }) }) - it("should reveal the deposit", async () => { - assertContractCalledWith(bridgeContract, "revealDeposit", [ - { - version: "0x00000000", - inputVector: "0x11111111", - outputVector: "0x22222222", - locktime: "0x33333333", - }, - { - fundingOutputIndex: 2, - blindingFactor: "0xf9f0c90d00039523", - walletPubKeyHash: "0x8db50eb52063ea9d98b3eac91489a90f738986f6", - refundPubKeyHash: "0x28e081f285138ccbe389c1eb8985716230129f89", - refundLocktime: "0x60bcea61", - vault: "0x82883a4c7a8dd73ef165deb402d432613615ced4", - }, - ]) + context("when deposit has optional extra data", () => { + beforeEach(async () => { + await bridgeContract.mock.revealDepositWithExtraData.returns() + + await bridgeHandle.revealDeposit( + // Just short byte strings for clarity. + { + version: Hex.from("00000000"), + inputs: Hex.from("11111111"), + outputs: Hex.from("22222222"), + locktime: Hex.from("33333333"), + }, + 2, + { + depositor: EthereumAddress.from( + "934b98637ca318a4d6e7ca6ffd1690b8e77df637" + ), + walletPublicKeyHash: Hex.from( + "8db50eb52063ea9d98b3eac91489a90f738986f6" + ), + refundPublicKeyHash: Hex.from( + "28e081f285138ccbe389c1eb8985716230129f89" + ), + blindingFactor: Hex.from("f9f0c90d00039523"), + refundLocktime: Hex.from("60bcea61"), + extraData: Hex.from( + "aebfb5afc9ee6432374ed39b58b8cf87797f9468eca40569b67ac8d59415c9c0" + ), + }, + EthereumAddress.from("82883a4c7a8dd73ef165deb402d432613615ced4") + ) + }) + + it("should reveal the deposit", async () => { + assertContractCalledWith( + bridgeContract, + "revealDepositWithExtraData", + [ + { + version: "0x00000000", + inputVector: "0x11111111", + outputVector: "0x22222222", + locktime: "0x33333333", + }, + { + fundingOutputIndex: 2, + blindingFactor: "0xf9f0c90d00039523", + walletPubKeyHash: "0x8db50eb52063ea9d98b3eac91489a90f738986f6", + refundPubKeyHash: "0x28e081f285138ccbe389c1eb8985716230129f89", + refundLocktime: "0x60bcea61", + vault: "0x82883a4c7a8dd73ef165deb402d432613615ced4", + }, + "0xaebfb5afc9ee6432374ed39b58b8cf87797f9468eca40569b67ac8d59415c9c0", + ] + ) + }) }) }) diff --git a/typescript/test/services/deposits.test.ts b/typescript/test/services/deposits.test.ts index 137fef2df..f7f08879c 100644 --- a/typescript/test/services/deposits.test.ts +++ b/typescript/test/services/deposits.test.ts @@ -15,6 +15,7 @@ import { BitcoinUtxo, Deposit, DepositFunding, + DepositorProxy, DepositReceipt, DepositRefund, DepositScript, @@ -32,6 +33,7 @@ import { depositRefundOfWitnessDepositAndWitnessRefunderAddress, refunderPrivateKey, } from "../data/deposit-refund" +import { MockDepositorProxy } from "../utils/mock-depositor-proxy" describe("Deposits", () => { const depositCreatedAt: number = 1640181600 @@ -39,91 +41,159 @@ describe("Deposits", () => { const depositAmount = BigNumber.from(10000) // 0.0001 BTC - const depositReceipt: DepositReceipt = { - depositor: EthereumAddress.from("934b98637ca318a4d6e7ca6ffd1690b8e77df637"), - // HASH160 of 03989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d9. - walletPublicKeyHash: Hex.from("8db50eb52063ea9d98b3eac91489a90f738986f6"), - // HASH160 of 0300d6f28a2f6bf9836f57fcda5d284c9a8f849316119779f0d6090830d97763a9. - refundPublicKeyHash: Hex.from("28e081f285138ccbe389c1eb8985716230129f89"), - blindingFactor: Hex.from("f9f0c90d00039523"), - refundLocktime: BitcoinLocktimeUtils.calculateLocktime( - depositCreatedAt, - depositRefundLocktimeDuration - ), - } - - // All test scenarios using the deposit script within `Deposit` group - // expect the same deposit script: - const expectedDepositScript = - "14934b98637ca318a4d6e7ca6ffd1690b8e77df6377508f9f0c90d000395237576a9148" + - "db50eb52063ea9d98b3eac91489a90f738986f68763ac6776a91428e081f285138ccbe3" + - "89c1eb8985716230129f89880460bcea61b175ac68" - - // Expected data of created deposit in P2WSH scenarios. - const expectedP2WSHDeposit = { - transactionHash: BitcoinTxHash.from( - "9eb901fc68f0d9bcaf575f23783b7d30ac5dd8d95f3c83dceaa13dce17de816a" - ), - - // HEX of the expected P2WSH deposit transaction. It can be decoded with: - // https://live.blockcypher.com/btc-testnet/decodetx. - transaction: { - transactionHex: - "010000000001018348cdeb551134fe1f19d378a8adec9b146671cb67b945b71bf56" + - "b20dc2b952f0100000000ffffffff021027000000000000220020df74a2e385542c" + - "87acfafa564ea4bc4fc4eb87d2b6a37d6c3b64722be83c636f10d73b00000000001" + - "600147ac2d9378a1c47e589dfb8095ca95ed2140d272602483045022100ac3d4148" + - "2338262654418825c37a4c7b327ed4e0b1dfb80eba0c98f264a6cc2e02201cd321f" + - "1b806cc946141d71b229dd0a440917c9f429b5f8840f7be59d70dbfee012102ee06" + - "7a0273f2e3ba88d23140a24fdb290f27bbcd0f94117a9c65be3911c5c04e0000000" + - "0", + const depositFixture = { + receipt: { + depositor: EthereumAddress.from( + "934b98637ca318a4d6e7ca6ffd1690b8e77df637" + ), + // HASH160 of 03989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d9. + walletPublicKeyHash: Hex.from("8db50eb52063ea9d98b3eac91489a90f738986f6"), + // HASH160 of 0300d6f28a2f6bf9836f57fcda5d284c9a8f849316119779f0d6090830d97763a9. + refundPublicKeyHash: Hex.from("28e081f285138ccbe389c1eb8985716230129f89"), + blindingFactor: Hex.from("f9f0c90d00039523"), + refundLocktime: BitcoinLocktimeUtils.calculateLocktime( + depositCreatedAt, + depositRefundLocktimeDuration + ), + extraData: undefined, + }, + expectedScript: + "14934b98637ca318a4d6e7ca6ffd1690b8e77df6377508f9f0c90d000395237576a91" + + "48db50eb52063ea9d98b3eac91489a90f738986f68763ac6776a91428e081f285138c" + + "cbe389c1eb8985716230129f89880460bcea61b175ac68", + expectedP2WSHData: { + transactionHash: BitcoinTxHash.from( + "9eb901fc68f0d9bcaf575f23783b7d30ac5dd8d95f3c83dceaa13dce17de816a" + ), + + // HEX of the expected P2WSH deposit transaction. It can be decoded with: + // https://live.blockcypher.com/btc-testnet/decodetx. + transaction: { + transactionHex: + "010000000001018348cdeb551134fe1f19d378a8adec9b146671cb67b945b71bf56" + + "b20dc2b952f0100000000ffffffff021027000000000000220020df74a2e385542c" + + "87acfafa564ea4bc4fc4eb87d2b6a37d6c3b64722be83c636f10d73b00000000001" + + "600147ac2d9378a1c47e589dfb8095ca95ed2140d272602483045022100ac3d4148" + + "2338262654418825c37a4c7b327ed4e0b1dfb80eba0c98f264a6cc2e02201cd321f" + + "1b806cc946141d71b229dd0a440917c9f429b5f8840f7be59d70dbfee012102ee06" + + "7a0273f2e3ba88d23140a24fdb290f27bbcd0f94117a9c65be3911c5c04e0000000" + + "0", + }, + + scriptHash: + "df74a2e385542c87acfafa564ea4bc4fc4eb87d2b6a37d6c3b64722be83c636f", + + mainnetAddress: + "bc1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhsdxuv4m", + + testnetAddress: + "tb1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhs6w2r05", + }, + expectedP2SHData: { + transactionHash: BitcoinTxHash.from( + "f21a9922c0c136c6d288cf1258b732d0f84a7d50d14a01d7d81cb6cd810f3517" + ), + + // HEX of the expected P2SH deposit transaction. It can be decoded with: + // https://live.blockcypher.com/btc-testnet/decodetx. + transaction: { + transactionHex: + "010000000001018348cdeb551134fe1f19d378a8adec9b146671cb67b945b71bf56" + + "b20dc2b952f0100000000ffffffff02102700000000000017a9142c1444d23936c5" + + "7bdd8b3e67e5938a5440cda455877ed73b00000000001600147ac2d9378a1c47e58" + + "9dfb8095ca95ed2140d27260247304402204582016a3cd3fa61fae1e1911b575625" + + "fe2ca75319de72349089724e80fb4a2f02207e76f992f64d0615779af763b157699" + + "a0d37270e136122408196084c1753a19e012102ee067a0273f2e3ba88d23140a24f" + + "db290f27bbcd0f94117a9c65be3911c5c04e00000000", + }, + + scriptHash: "2c1444d23936c57bdd8b3e67e5938a5440cda455", + + mainnetAddress: "35i5wHdLir1hdjCr6hiQNk3yTH9ufe61eH", + + testnetAddress: "2MwGJ12ZNLJX3qWqPmqLGzh3EfdN5XAEGQ8", }, - - scriptHash: - "df74a2e385542c87acfafa564ea4bc4fc4eb87d2b6a37d6c3b64722be83c636f", - - mainnetAddress: - "bc1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhsdxuv4m", - - testnetAddress: - "tb1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhs6w2r05", } - // Expected data of created deposit in P2SH scenarios. - const expectedP2SHDeposit = { - transactionHash: BitcoinTxHash.from( - "f21a9922c0c136c6d288cf1258b732d0f84a7d50d14a01d7d81cb6cd810f3517" - ), - - // HEX of the expected P2SH deposit transaction. It can be decoded with: - // https://live.blockcypher.com/btc-testnet/decodetx. - transaction: { - transactionHex: - "010000000001018348cdeb551134fe1f19d378a8adec9b146671cb67b945b71bf56" + - "b20dc2b952f0100000000ffffffff02102700000000000017a9142c1444d23936c5" + - "7bdd8b3e67e5938a5440cda455877ed73b00000000001600147ac2d9378a1c47e58" + - "9dfb8095ca95ed2140d27260247304402204582016a3cd3fa61fae1e1911b575625" + - "fe2ca75319de72349089724e80fb4a2f02207e76f992f64d0615779af763b157699" + - "a0d37270e136122408196084c1753a19e012102ee067a0273f2e3ba88d23140a24f" + - "db290f27bbcd0f94117a9c65be3911c5c04e00000000", + const depositWithExtraDataFixture = { + receipt: { + ...depositFixture.receipt, + extraData: Hex.from( + "a9b38ea6435c8941d6eda6a46b68e3e2117196995bd154ab55196396b03d9bda" + ), + }, + expectedScript: + "14934b98637ca318a4d6e7ca6ffd1690b8e77df6377520a9b38ea6435c8941d6eda6a46" + + "b68e3e2117196995bd154ab55196396b03d9bda7508f9f0c90d000395237576a9148db5" + + "0eb52063ea9d98b3eac91489a90f738986f68763ac6776a91428e081f285138ccbe389c" + + "1eb8985716230129f89880460bcea61b175ac68", + expectedP2WSHData: { + transactionHash: BitcoinTxHash.from( + "32577973ff05a55f72b19378baaafb04e009c4ac512def4453d5d8d0032131c9" + ), + + // HEX of the expected P2WSH deposit transaction. It can be decoded with: + // https://live.blockcypher.com/btc-testnet/decodetx. + transaction: { + transactionHex: + "010000000001018348cdeb551134fe1f19d378a8adec9b146671cb67b945b71bf56" + + "b20dc2b952f0100000000ffffffff021027000000000000220020bfaeddba12b0de" + + "6feeb649af76376876bc1feb6c2248fbfef9293ba3ac51bb4a10d73b00000000001" + + "600147ac2d9378a1c47e589dfb8095ca95ed2140d272602483045022100bccb772b" + + "4149b9ce46f426849177f81e359b3f01491dce7ed38474f356a021e202205ed8aee" + + "19ba5e56f324ab19926a21b05e977210b52cd77282f0de3a879f21f0f012102ee06" + + "7a0273f2e3ba88d23140a24fdb290f27bbcd0f94117a9c65be3911c5c04e0000000" + + "0", + }, + + scriptHash: + "bfaeddba12b0de6feeb649af76376876bc1feb6c2248fbfef9293ba3ac51bb4a", + + mainnetAddress: + "bc1qh7hdmwsjkr0xlm4kfxhhvdmgw67pl6mvyfy0hlhe9ya68tz3hd9q7vkpyw", + + testnetAddress: + "tb1qh7hdmwsjkr0xlm4kfxhhvdmgw67pl6mvyfy0hlhe9ya68tz3hd9qfyqw7p", + }, + expectedP2SHData: { + transactionHash: BitcoinTxHash.from( + "bcdf1661f9623c46b6fc578d6a3c8c8e747161d3ba12cd34600b262918cd8363" + ), + + // HEX of the expected P2SH deposit transaction. It can be decoded with: + // https://live.blockcypher.com/btc-testnet/decodetx. + transaction: { + transactionHex: + "010000000001018348cdeb551134fe1f19d378a8adec9b146671cb67b945b71bf56" + + "b20dc2b952f0100000000ffffffff02102700000000000017a9149fe6615a307aa1" + + "d7eee668c1227802b2fbcaa919877ed73b00000000001600147ac2d9378a1c47e58" + + "9dfb8095ca95ed2140d27260247304402200c3e81879621451c4392ed525b2f8514" + + "a011b35c505299f8b8520b176b8b772d022013dd63eeafa18a19ccfc102f6e85363" + + "a89e6e9515a2494dc32bca32248e44789012102ee067a0273f2e3ba88d23140a24f" + + "db290f27bbcd0f94117a9c65be3911c5c04e00000000", + }, + + scriptHash: "9fe6615a307aa1d7eee668c1227802b2fbcaa919", + + mainnetAddress: "3GGVM9WERE3wsN2QfUJxPzhWFtLicRGhHi", + + testnetAddress: "2N7phQtSG2gZJ59exLbvq1wgmUEYtRc51Ly", }, - - scriptHash: "2c1444d23936c57bdd8b3e67e5938a5440cda455", - - mainnetAddress: "35i5wHdLir1hdjCr6hiQNk3yTH9ufe61eH", - - testnetAddress: "2MwGJ12ZNLJX3qWqPmqLGzh3EfdN5XAEGQ8", } /** * Checks if script given in argument is correct * @param script - script as an un-prefixed hex string. + * @param fixture - fixture the script should be validated against. * @returns void */ - function assertValidDepositScript(script: string): void { + function assertValidDepositScript( + script: string, + fixture: typeof depositFixture | typeof depositWithExtraDataFixture + ): void { // Returned script should be the same as expectedDepositScript but // here we make a breakdown and assert specific parts are as expected. - expect(script.length).to.be.equal(expectedDepositScript.length) + expect(script.length).to.be.equal(fixture.expectedScript.length) // Assert the depositor identifier is encoded correctly. // According the Bitcoin script format, the first byte before arbitrary @@ -132,72 +202,90 @@ describe("Deposits", () => { // have a 20 bytes depositor identifier as subsequent data. expect(script.substring(0, 2)).to.be.equal("14") expect(script.substring(2, 42)).to.be.equal( - depositReceipt.depositor.identifierHex + fixture.receipt.depositor.identifierHex ) // According to https://en.bitcoin.it/wiki/Script#Constants, the // OP_DROP opcode is 0x75. expect(script.substring(42, 44)).to.be.equal("75") + let offset = 0 + if (typeof fixture.receipt.extraData !== "undefined") { + // Assert optional extra data is encoded correctly. The first byte + // is 0x20 which is 32 in decimal, and this is correct because we + // have a 32-byte extra data as subsequent vector. + expect(script.substring(44, 46)).to.be.equal("20") + expect(script.substring(46, 110)).to.be.equal( + (fixture.receipt.extraData as Hex).toString() + ) + + // OP_DROP opcode is 0x75. + expect(script.substring(110, 112)).to.be.equal("75") + + // If extra data is present, we need to offset the following checks by + // 1 + 32 + 1 = 34 bytes. That gives 68 characters. + offset = 68 + } + // Assert the blinding factor is encoded correctly. // The first byte (0x08) before the blinding factor is this byte length. // In this case it's 8 bytes. - expect(script.substring(44, 46)).to.be.equal("08") - expect(script.substring(46, 62)).to.be.equal( - depositReceipt.blindingFactor.toString() + expect(script.substring(44 + offset, 46 + offset)).to.be.equal("08") + expect(script.substring(46 + offset, 62 + offset)).to.be.equal( + fixture.receipt.blindingFactor.toString() ) // OP_DROP opcode is 0x75. - expect(script.substring(62, 64)).to.be.equal("75") + expect(script.substring(62 + offset, 64 + offset)).to.be.equal("75") // OP_DUP opcode is 0x76. - expect(script.substring(64, 66)).to.be.equal("76") + expect(script.substring(64 + offset, 66 + offset)).to.be.equal("76") // OP_HASH160 opcode is 0xa9. - expect(script.substring(66, 68)).to.be.equal("a9") + expect(script.substring(66 + offset, 68 + offset)).to.be.equal("a9") // Assert the wallet public key hash is encoded correctly. // The first byte (0x14) before the public key is this byte length. // In this case it's 20 bytes which is a correct length for a HASH160. - expect(script.substring(68, 70)).to.be.equal("14") - expect(script.substring(70, 110)).to.be.equal( - depositReceipt.walletPublicKeyHash.toString() + expect(script.substring(68 + offset, 70 + offset)).to.be.equal("14") + expect(script.substring(70 + offset, 110 + offset)).to.be.equal( + fixture.receipt.walletPublicKeyHash.toString() ) // OP_EQUAL opcode is 0x87. - expect(script.substring(110, 112)).to.be.equal("87") + expect(script.substring(110 + offset, 112 + offset)).to.be.equal("87") // OP_IF opcode is 0x63. - expect(script.substring(112, 114)).to.be.equal("63") + expect(script.substring(112 + offset, 114 + offset)).to.be.equal("63") // OP_CHECKSIG opcode is 0xac. - expect(script.substring(114, 116)).to.be.equal("ac") + expect(script.substring(114 + offset, 116 + offset)).to.be.equal("ac") // OP_ELSE opcode is 0x67. - expect(script.substring(116, 118)).to.be.equal("67") + expect(script.substring(116 + offset, 118 + offset)).to.be.equal("67") // OP_DUP opcode is 0x76. - expect(script.substring(118, 120)).to.be.equal("76") + expect(script.substring(118 + offset, 120 + offset)).to.be.equal("76") // OP_HASH160 opcode is 0xa9. - expect(script.substring(120, 122)).to.be.equal("a9") + expect(script.substring(120 + offset, 122 + offset)).to.be.equal("a9") // Assert the refund public key hash is encoded correctly. // The first byte (0x14) before the public key is this byte length. // In this case it's 20 bytes which is a correct length for a HASH160. - expect(script.substring(122, 124)).to.be.equal("14") - expect(script.substring(124, 164)).to.be.equal( - depositReceipt.refundPublicKeyHash.toString() + expect(script.substring(122 + offset, 124 + offset)).to.be.equal("14") + expect(script.substring(124 + offset, 164 + offset)).to.be.equal( + fixture.receipt.refundPublicKeyHash.toString() ) // OP_EQUALVERIFY opcode is 0x88. - expect(script.substring(164, 166)).to.be.equal("88") + expect(script.substring(164 + offset, 166 + offset)).to.be.equal("88") // Assert the locktime is encoded correctly. // The first byte (0x04) before the locktime is this byte length. // In this case it's 4 bytes. - expect(script.substring(166, 168)).to.be.equal("04") - expect(script.substring(168, 176)).to.be.equal( + expect(script.substring(166 + offset, 168 + offset)).to.be.equal("04") + expect(script.substring(168 + offset, 176 + offset)).to.be.equal( Buffer.from( BigNumber.from(1640181600 + 2592000) .toHexString() @@ -209,16 +297,16 @@ describe("Deposits", () => { ) // OP_CHECKLOCKTIMEVERIFY opcode is 0xb1. - expect(script.substring(176, 178)).to.be.equal("b1") + expect(script.substring(176 + offset, 178 + offset)).to.be.equal("b1") // OP_DROP opcode is 0x75. - expect(script.substring(178, 180)).to.be.equal("75") + expect(script.substring(178 + offset, 180 + offset)).to.be.equal("75") // OP_CHECKSIG opcode is 0xac. - expect(script.substring(180, 182)).to.be.equal("ac") + expect(script.substring(180 + offset, 182 + offset)).to.be.equal("ac") // OP_ENDIF opcode is 0x68. - expect(script.substring(182, 184)).to.be.equal("68") + expect(script.substring(182 + offset, 184 + offset)).to.be.equal("68") } describe("DepositFunding", () => { @@ -229,7 +317,7 @@ describe("Deposits", () => { bitcoinClient = new MockBitcoinClient() // Tie testnetTransaction to testnetUTXO. This is needed since - // submitDepositTransaction attach transaction data to each UTXO. + // DepositFunding.submitTransaction attach transaction data to each UTXO. const rawTransactions = new Map() rawTransactions.set( testnetTransactionHash.toString(), @@ -238,301 +326,615 @@ describe("Deposits", () => { bitcoinClient.rawTransactions = rawTransactions }) - context("when witness option is true", () => { - let transactionHash: BitcoinTxHash - let depositUtxo: BitcoinUtxo + context("when deposit does not have optional extra data", () => { + context("when witness option is true", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo - beforeEach(async () => { - const fee = BigNumber.from(1520) + beforeEach(async () => { + const fee = BigNumber.from(1520) - const depositFunding = DepositFunding.fromScript( - DepositScript.fromReceipt(depositReceipt, true) - ) + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt(depositFixture.receipt, true) + ) - ;({ transactionHash, depositUtxo } = - await depositFunding.submitTransaction( - depositAmount, - [testnetUTXO], - fee, - testnetPrivateKey, - bitcoinClient - )) + ;({ transactionHash, depositUtxo } = + await depositFunding.submitTransaction( + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey, + bitcoinClient + )) + }) + + it("should broadcast P2WSH transaction with proper structure", async () => { + expect(bitcoinClient.broadcastLog.length).to.be.equal(1) + expect(bitcoinClient.broadcastLog[0]).to.be.eql( + depositFixture.expectedP2WSHData.transaction + ) + }) + + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositFixture.expectedP2WSHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: depositFixture.expectedP2WSHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.eql(expectedDepositUtxo) + }) }) - it("should broadcast P2WSH transaction with proper structure", async () => { - expect(bitcoinClient.broadcastLog.length).to.be.equal(1) - expect(bitcoinClient.broadcastLog[0]).to.be.eql( - expectedP2WSHDeposit.transaction - ) + context("when witness option is false", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + + beforeEach(async () => { + const fee = BigNumber.from(1410) + + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt(depositFixture.receipt, false) + ) + + ;({ transactionHash, depositUtxo } = + await depositFunding.submitTransaction( + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey, + bitcoinClient + )) + }) + + it("should broadcast P2SH transaction with proper structure", async () => { + expect(bitcoinClient.broadcastLog.length).to.be.equal(1) + expect(bitcoinClient.broadcastLog[0]).to.be.eql( + depositFixture.expectedP2SHData.transaction + ) + }) + + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositFixture.expectedP2SHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: depositFixture.expectedP2SHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.eql(expectedDepositUtxo) + }) }) + }) - it("should return the proper transaction hash", async () => { - expect(transactionHash).to.be.deep.equal( - expectedP2WSHDeposit.transactionHash - ) + context("when deposit has optional extra data", () => { + context("when witness option is true", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + + beforeEach(async () => { + const fee = BigNumber.from(1520) + + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + true + ) + ) + + ;({ transactionHash, depositUtxo } = + await depositFunding.submitTransaction( + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey, + bitcoinClient + )) + }) + + it("should broadcast P2WSH transaction with proper structure", async () => { + expect(bitcoinClient.broadcastLog.length).to.be.equal(1) + expect(bitcoinClient.broadcastLog[0]).to.be.eql( + depositWithExtraDataFixture.expectedP2WSHData.transaction + ) + }) + + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositWithExtraDataFixture.expectedP2WSHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: + depositWithExtraDataFixture.expectedP2WSHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.eql(expectedDepositUtxo) + }) }) - it("should return the proper deposit UTXO", () => { - const expectedDepositUtxo = { - transactionHash: expectedP2WSHDeposit.transactionHash, - outputIndex: 0, - value: depositAmount, - } + context("when witness option is false", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + + beforeEach(async () => { + const fee = BigNumber.from(1410) + + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + false + ) + ) - expect(depositUtxo).to.be.eql(expectedDepositUtxo) + ;({ transactionHash, depositUtxo } = + await depositFunding.submitTransaction( + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey, + bitcoinClient + )) + }) + + it("should broadcast P2SH transaction with proper structure", async () => { + expect(bitcoinClient.broadcastLog.length).to.be.equal(1) + expect(bitcoinClient.broadcastLog[0]).to.be.eql( + depositWithExtraDataFixture.expectedP2SHData.transaction + ) + }) + + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositWithExtraDataFixture.expectedP2SHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: + depositWithExtraDataFixture.expectedP2SHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.eql(expectedDepositUtxo) + }) }) }) + }) - context("when witness option is false", () => { - let transactionHash: BitcoinTxHash - let depositUtxo: BitcoinUtxo + describe("assembleTransaction", () => { + context("when deposit does not have optional extra data", () => { + context("when witness option is true", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + let transaction: BitcoinRawTx - beforeEach(async () => { - const fee = BigNumber.from(1410) + beforeEach(async () => { + const fee = BigNumber.from(1520) - const depositFunding = DepositFunding.fromScript( - DepositScript.fromReceipt(depositReceipt, false) - ) + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt(depositFixture.receipt, true) + ) - ;({ transactionHash, depositUtxo } = - await depositFunding.submitTransaction( + ;({ + transactionHash, + depositUtxo, + rawTransaction: transaction, + } = await depositFunding.assembleTransaction( + BitcoinNetwork.Testnet, depositAmount, [testnetUTXO], fee, - testnetPrivateKey, - bitcoinClient + testnetPrivateKey )) - }) + }) - it("should broadcast P2SH transaction with proper structure", async () => { - expect(bitcoinClient.broadcastLog.length).to.be.equal(1) - expect(bitcoinClient.broadcastLog[0]).to.be.eql( - expectedP2SHDeposit.transaction - ) - }) + it("should return P2WSH transaction with proper structure", async () => { + // Compare HEXes. + expect(transaction).to.be.eql( + depositFixture.expectedP2WSHData.transaction + ) - it("should return the proper transaction hash", async () => { - expect(transactionHash).to.be.deep.equal( - expectedP2SHDeposit.transactionHash - ) - }) + // Convert raw transaction to JSON to make detailed comparison. + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) - it("should return the proper deposit UTXO", () => { - const expectedDepositUtxo = { - transactionHash: expectedP2SHDeposit.transactionHash, - outputIndex: 0, - value: depositAmount, - } + expect(txJSON.hash).to.be.equal( + depositFixture.expectedP2WSHData.transactionHash.toString() + ) + expect(txJSON.version).to.be.equal(1) - expect(depositUtxo).to.be.eql(expectedDepositUtxo) - }) - }) - }) + // Validate inputs. + expect(txJSON.inputs.length).to.be.equal(1) - describe("assembleTransaction", () => { - context("when witness option is true", () => { - let transactionHash: BitcoinTxHash - let depositUtxo: BitcoinUtxo - let transaction: BitcoinRawTx + const input = txJSON.inputs[0] - beforeEach(async () => { - const fee = BigNumber.from(1520) + expect(input.hash).to.be.equal( + testnetUTXO.transactionHash.toString() + ) + expect(input.index).to.be.equal(testnetUTXO.outputIndex) + // Transaction should be signed but this is SegWit input so the `script` + // field should be empty and the `witness` field should be filled instead. + expect(input.script.length).to.be.equal(0) + expect(input.witness.length).to.be.greaterThan(0) + + // Validate outputs. + expect(txJSON.outputs.length).to.be.equal(2) + + const depositOutput = txJSON.outputs[0] + const changeOutput = txJSON.outputs[1] + + // Value should correspond to the deposit amount. + expect(depositOutput.value).to.be.equal(depositAmount.toNumber()) + // Should be OP_0 . The script hash should be prefixed + // with its byte length: 0x20. The OP_0 opcode is 0x00. + expect(depositOutput.script).to.be.equal( + `0020${depositFixture.expectedP2WSHData.scriptHash}` + ) + expect(depositOutput.address).to.be.equal( + depositFixture.expectedP2WSHData.testnetAddress + ) - const depositFunding = DepositFunding.fromScript( - DepositScript.fromReceipt(depositReceipt, true) - ) + // Change value should be equal to: inputValue - depositAmount - fee. + expect(changeOutput.value).to.be.equal(3921680) + // Should be OP_0 . Public key corresponds to + // depositor BTC address. + expect(changeOutput.script).to.be.equal( + "00147ac2d9378a1c47e589dfb8095ca95ed2140d2726" + ) + // Should return the change to depositor BTC address. + expect(changeOutput.address).to.be.equal(testnetAddress) + }) - ;({ - transactionHash, - depositUtxo, - rawTransaction: transaction, - } = await depositFunding.assembleTransaction( - BitcoinNetwork.Testnet, - depositAmount, - [testnetUTXO], - fee, - testnetPrivateKey - )) + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositFixture.expectedP2WSHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: depositFixture.expectedP2WSHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.eql(expectedDepositUtxo) + }) }) - it("should return P2WSH transaction with proper structure", async () => { - // Compare HEXes. - expect(transaction).to.be.eql(expectedP2WSHDeposit.transaction) + context("when witness option is false", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + let transaction: BitcoinRawTx - // Convert raw transaction to JSON to make detailed comparison. - const txJSON = txToJSON( - transaction.transactionHex, - BitcoinNetwork.Testnet - ) + beforeEach(async () => { + const fee = BigNumber.from(1410) - expect(txJSON.hash).to.be.equal( - expectedP2WSHDeposit.transactionHash.toString() - ) - expect(txJSON.version).to.be.equal(1) - - // Validate inputs. - expect(txJSON.inputs.length).to.be.equal(1) - - const input = txJSON.inputs[0] - - expect(input.hash).to.be.equal(testnetUTXO.transactionHash.toString()) - expect(input.index).to.be.equal(testnetUTXO.outputIndex) - // Transaction should be signed but this is SegWit input so the `script` - // field should be empty and the `witness` field should be filled instead. - expect(input.script.length).to.be.equal(0) - expect(input.witness.length).to.be.greaterThan(0) - - // Validate outputs. - expect(txJSON.outputs.length).to.be.equal(2) - - const depositOutput = txJSON.outputs[0] - const changeOutput = txJSON.outputs[1] - - // Value should correspond to the deposit amount. - expect(depositOutput.value).to.be.equal(depositAmount.toNumber()) - // Should be OP_0 . The script hash is the same as in - // expectedP2WSHDeposit.scriptHash (see DepositScript.getHash - // witness scenario) and it should be prefixed with its byte length: - // 0x20. The OP_0 opcode is 0x00. - expect(depositOutput.script).to.be.equal( - `0020${expectedP2WSHDeposit.scriptHash}` - ) - // The address should correspond to the script hash - // expectedP2WSHDeposit.scriptHash on testnet so it should be: - // expectedP2WSHDeposit.testnetAddress (see DepositScript.deriveAddress - // witness scenario). - expect(depositOutput.address).to.be.equal( - expectedP2WSHDeposit.testnetAddress - ) + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt(depositFixture.receipt, false) + ) - // Change value should be equal to: inputValue - depositAmount - fee. - expect(changeOutput.value).to.be.equal(3921680) - // Should be OP_0 . Public key corresponds to - // depositor BTC address. - expect(changeOutput.script).to.be.equal( - "00147ac2d9378a1c47e589dfb8095ca95ed2140d2726" - ) - // Should return the change to depositor BTC address. - expect(changeOutput.address).to.be.equal(testnetAddress) - }) + ;({ + transactionHash, + depositUtxo, + rawTransaction: transaction, + } = await depositFunding.assembleTransaction( + BitcoinNetwork.Testnet, + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey + )) + }) - it("should return the proper transaction hash", async () => { - expect(transactionHash).to.be.deep.equal( - expectedP2WSHDeposit.transactionHash - ) - }) + it("should return P2SH transaction with proper structure", async () => { + // Compare HEXes. + expect(transaction).to.be.eql( + depositFixture.expectedP2SHData.transaction + ) - it("should return the proper deposit UTXO", () => { - const expectedDepositUtxo = { - transactionHash: expectedP2WSHDeposit.transactionHash, - outputIndex: 0, - value: depositAmount, - } + // Convert raw transaction to JSON to make detailed comparison. + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) + + expect(txJSON.hash).to.be.equal( + depositFixture.expectedP2SHData.transactionHash.toString() + ) + expect(txJSON.version).to.be.equal(1) + + // Validate inputs. + expect(txJSON.inputs.length).to.be.equal(1) + + const input = txJSON.inputs[0] + + expect(input.hash).to.be.equal( + testnetUTXO.transactionHash.toString() + ) + expect(input.index).to.be.equal(testnetUTXO.outputIndex) + // Transaction should be signed but this is SegWit input so the `script` + // field should be empty and the `witness` field should be filled instead. + expect(input.script.length).to.be.equal(0) + expect(input.witness.length).to.be.greaterThan(0) + + // Validate outputs. + expect(txJSON.outputs.length).to.be.equal(2) + + const depositOutput = txJSON.outputs[0] + const changeOutput = txJSON.outputs[1] + + // Value should correspond to the deposit amount. + expect(depositOutput.value).to.be.equal(depositAmount.toNumber()) + // Should be OP_HASH160 OP_EQUAL. The script hash + // should be prefixed with its byte length: 0x14. The OP_HASH160 + // opcode is 0xa9 and OP_EQUAL is 0x87. + expect(depositOutput.script).to.be.equal( + `a914${depositFixture.expectedP2SHData.scriptHash}87` + ) + expect(depositOutput.address).to.be.equal( + depositFixture.expectedP2SHData.testnetAddress + ) + + // Change value should be equal to: inputValue - depositAmount - fee. + expect(changeOutput.value).to.be.equal(3921790) + // Should be OP_0 . Public key corresponds to + // depositor BTC address. + expect(changeOutput.script).to.be.equal( + "00147ac2d9378a1c47e589dfb8095ca95ed2140d2726" + ) + // Should return the change to depositor BTC address. + expect(changeOutput.address).to.be.equal(testnetAddress) + }) - expect(depositUtxo).to.be.eql(expectedDepositUtxo) + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositFixture.expectedP2SHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: depositFixture.expectedP2SHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.deep.equal(expectedDepositUtxo) + }) }) }) - context("when witness option is false", () => { - let transactionHash: BitcoinTxHash - let depositUtxo: BitcoinUtxo - let transaction: BitcoinRawTx + context("when deposit has optional extra data", () => { + context("when witness option is true", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + let transaction: BitcoinRawTx - beforeEach(async () => { - const fee = BigNumber.from(1410) + beforeEach(async () => { + const fee = BigNumber.from(1520) - const depositFunding = DepositFunding.fromScript( - DepositScript.fromReceipt(depositReceipt, false) - ) + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + true + ) + ) - ;({ - transactionHash, - depositUtxo, - rawTransaction: transaction, - } = await depositFunding.assembleTransaction( - BitcoinNetwork.Testnet, - depositAmount, - [testnetUTXO], - fee, - testnetPrivateKey - )) - }) + ;({ + transactionHash, + depositUtxo, + rawTransaction: transaction, + } = await depositFunding.assembleTransaction( + BitcoinNetwork.Testnet, + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey + )) + }) - it("should return P2SH transaction with proper structure", async () => { - // Compare HEXes. - expect(transaction).to.be.eql(expectedP2SHDeposit.transaction) + it("should return P2WSH transaction with proper structure", async () => { + // Compare HEXes. + expect(transaction).to.be.eql( + depositWithExtraDataFixture.expectedP2WSHData.transaction + ) - // Convert raw transaction to JSON to make detailed comparison. - const txJSON = txToJSON( - transaction.transactionHex, - BitcoinNetwork.Testnet - ) + // Convert raw transaction to JSON to make detailed comparison. + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) - expect(txJSON.hash).to.be.equal( - expectedP2SHDeposit.transactionHash.toString() - ) - expect(txJSON.version).to.be.equal(1) - - // Validate inputs. - expect(txJSON.inputs.length).to.be.equal(1) - - const input = txJSON.inputs[0] - - expect(input.hash).to.be.equal(testnetUTXO.transactionHash.toString()) - expect(input.index).to.be.equal(testnetUTXO.outputIndex) - // Transaction should be signed but this is SegWit input so the `script` - // field should be empty and the `witness` field should be filled instead. - expect(input.script.length).to.be.equal(0) - expect(input.witness.length).to.be.greaterThan(0) - - // Validate outputs. - expect(txJSON.outputs.length).to.be.equal(2) - - const depositOutput = txJSON.outputs[0] - const changeOutput = txJSON.outputs[1] - - // Value should correspond to the deposit amount. - expect(depositOutput.value).to.be.equal(depositAmount.toNumber()) - // Should be OP_HASH160 OP_EQUAL. The script hash is - // expectedP2SHDeposit.scriptHash (see DepositScript.getHash - // non-witness scenario) and it should be prefixed with its byte - // length: 0x14. The OP_HASH160 opcode is 0xa9 and OP_EQUAL is 0x87. - expect(depositOutput.script).to.be.equal( - `a914${expectedP2SHDeposit.scriptHash}87` - ) - // The address should correspond to the script hash - // expectedP2SHDeposit.scriptHash on testnet so it should be - // expectedP2SHDeposit.testnetAddress (see DepositScript.deriveAddress - // non-witness scenario). - expect(depositOutput.address).to.be.equal( - expectedP2SHDeposit.testnetAddress - ) + expect(txJSON.hash).to.be.equal( + depositWithExtraDataFixture.expectedP2WSHData.transactionHash.toString() + ) + expect(txJSON.version).to.be.equal(1) - // Change value should be equal to: inputValue - depositAmount - fee. - expect(changeOutput.value).to.be.equal(3921790) - // Should be OP_0 . Public key corresponds to - // depositor BTC address. - expect(changeOutput.script).to.be.equal( - "00147ac2d9378a1c47e589dfb8095ca95ed2140d2726" - ) - // Should return the change to depositor BTC address. - expect(changeOutput.address).to.be.equal(testnetAddress) - }) + // Validate inputs. + expect(txJSON.inputs.length).to.be.equal(1) - it("should return the proper transaction hash", async () => { - expect(transactionHash).to.be.deep.equal( - expectedP2SHDeposit.transactionHash - ) + const input = txJSON.inputs[0] + + expect(input.hash).to.be.equal( + testnetUTXO.transactionHash.toString() + ) + expect(input.index).to.be.equal(testnetUTXO.outputIndex) + // Transaction should be signed but this is SegWit input so the `script` + // field should be empty and the `witness` field should be filled instead. + expect(input.script.length).to.be.equal(0) + expect(input.witness.length).to.be.greaterThan(0) + + // Validate outputs. + expect(txJSON.outputs.length).to.be.equal(2) + + const depositOutput = txJSON.outputs[0] + const changeOutput = txJSON.outputs[1] + + // Value should correspond to the deposit amount. + expect(depositOutput.value).to.be.equal(depositAmount.toNumber()) + // Should be OP_0 . The script hash should be prefixed + // with its byte length: 0x20. The OP_0 opcode is 0x00. + expect(depositOutput.script).to.be.equal( + `0020${depositWithExtraDataFixture.expectedP2WSHData.scriptHash}` + ) + expect(depositOutput.address).to.be.equal( + depositWithExtraDataFixture.expectedP2WSHData.testnetAddress + ) + + // Change value should be equal to: inputValue - depositAmount - fee. + expect(changeOutput.value).to.be.equal(3921680) + // Should be OP_0 . Public key corresponds to + // depositor BTC address. + expect(changeOutput.script).to.be.equal( + "00147ac2d9378a1c47e589dfb8095ca95ed2140d2726" + ) + // Should return the change to depositor BTC address. + expect(changeOutput.address).to.be.equal(testnetAddress) + }) + + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositWithExtraDataFixture.expectedP2WSHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: + depositWithExtraDataFixture.expectedP2WSHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.eql(expectedDepositUtxo) + }) }) - it("should return the proper deposit UTXO", () => { - const expectedDepositUtxo = { - transactionHash: expectedP2SHDeposit.transactionHash, - outputIndex: 0, - value: depositAmount, - } + context("when witness option is false", () => { + let transactionHash: BitcoinTxHash + let depositUtxo: BitcoinUtxo + let transaction: BitcoinRawTx + + beforeEach(async () => { + const fee = BigNumber.from(1410) + + const depositFunding = DepositFunding.fromScript( + DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + false + ) + ) + + ;({ + transactionHash, + depositUtxo, + rawTransaction: transaction, + } = await depositFunding.assembleTransaction( + BitcoinNetwork.Testnet, + depositAmount, + [testnetUTXO], + fee, + testnetPrivateKey + )) + }) + + it("should return P2SH transaction with proper structure", async () => { + // Compare HEXes. + expect(transaction).to.be.eql( + depositWithExtraDataFixture.expectedP2SHData.transaction + ) + + // Convert raw transaction to JSON to make detailed comparison. + const txJSON = txToJSON( + transaction.transactionHex, + BitcoinNetwork.Testnet + ) + + expect(txJSON.hash).to.be.equal( + depositWithExtraDataFixture.expectedP2SHData.transactionHash.toString() + ) + expect(txJSON.version).to.be.equal(1) - expect(depositUtxo).to.be.deep.equal(expectedDepositUtxo) + // Validate inputs. + expect(txJSON.inputs.length).to.be.equal(1) + + const input = txJSON.inputs[0] + + expect(input.hash).to.be.equal( + testnetUTXO.transactionHash.toString() + ) + expect(input.index).to.be.equal(testnetUTXO.outputIndex) + // Transaction should be signed but this is SegWit input so the `script` + // field should be empty and the `witness` field should be filled instead. + expect(input.script.length).to.be.equal(0) + expect(input.witness.length).to.be.greaterThan(0) + + // Validate outputs. + expect(txJSON.outputs.length).to.be.equal(2) + + const depositOutput = txJSON.outputs[0] + const changeOutput = txJSON.outputs[1] + + // Value should correspond to the deposit amount. + expect(depositOutput.value).to.be.equal(depositAmount.toNumber()) + // Should be OP_HASH160 OP_EQUAL. The script hash + // should be prefixed with its byte length: 0x14. The OP_HASH160 + // opcode is 0xa9 and OP_EQUAL is 0x87. + expect(depositOutput.script).to.be.equal( + `a914${depositWithExtraDataFixture.expectedP2SHData.scriptHash}87` + ) + expect(depositOutput.address).to.be.equal( + depositWithExtraDataFixture.expectedP2SHData.testnetAddress + ) + + // Change value should be equal to: inputValue - depositAmount - fee. + expect(changeOutput.value).to.be.equal(3921790) + // Should be OP_0 . Public key corresponds to + // depositor BTC address. + expect(changeOutput.script).to.be.equal( + "00147ac2d9378a1c47e589dfb8095ca95ed2140d2726" + ) + // Should return the change to depositor BTC address. + expect(changeOutput.address).to.be.equal(testnetAddress) + }) + + it("should return the proper transaction hash", async () => { + expect(transactionHash).to.be.deep.equal( + depositWithExtraDataFixture.expectedP2SHData.transactionHash + ) + }) + + it("should return the proper deposit UTXO", () => { + const expectedDepositUtxo = { + transactionHash: + depositWithExtraDataFixture.expectedP2SHData.transactionHash, + outputIndex: 0, + value: depositAmount, + } + + expect(depositUtxo).to.be.deep.equal(expectedDepositUtxo) + }) }) }) }) @@ -542,61 +944,124 @@ describe("Deposits", () => { describe("getPlainText", () => { let script: Hex - beforeEach(async () => { - script = await DepositScript.fromReceipt(depositReceipt).getPlainText() + context("when deposit does not have optional extra data", () => { + beforeEach(async () => { + script = await DepositScript.fromReceipt( + depositFixture.receipt + ).getPlainText() + }) + + it("should return script with proper structure", async () => { + assertValidDepositScript(script.toString(), depositFixture) + }) }) - it("should return script with proper structure", async () => { - assertValidDepositScript(script.toString()) + context("when deposit has optional extra data", () => { + beforeEach(async () => { + script = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt + ).getPlainText() + }) + + it("should return script with proper structure", async () => { + assertValidDepositScript( + script.toString(), + depositWithExtraDataFixture + ) + }) }) }) describe("getHash", () => { - context("when witness option is true", () => { - let scriptHash: Buffer + context("when deposit does not have optional extra data", () => { + context("when witness option is true", () => { + let scriptHash: Buffer - beforeEach(async () => { - scriptHash = await DepositScript.fromReceipt( - depositReceipt, - true - ).getHash() + beforeEach(async () => { + scriptHash = await DepositScript.fromReceipt( + depositFixture.receipt, + true + ).getHash() + }) + + it("should return proper witness script hash", async () => { + // The plain-text script is in the expectedScript property of the fixture. + // The hash of this script should correspond to the OP_SHA256 opcode + // which applies SHA-256 on the input. The hash can be verified with + // the following command: + // echo -n $SCRIPT | xxd -r -p | openssl dgst -sha256 + expect(scriptHash.toString("hex")).to.be.equal( + depositFixture.expectedP2WSHData.scriptHash + ) + }) }) - it("should return proper witness script hash", async () => { - // The script for given deposit should be the same as in - // assembleDepositScript test scenario i.e. expectedDepositScript. - // The hash of this script should correspond to the OP_SHA256 opcode - // which applies SHA-256 on the input. In this case the hash is - // expectedP2WSHDeposit.scriptHash and it can be verified with - // the following command: - // echo -n $SCRIPT | xxd -r -p | openssl dgst -sha256 - expect(scriptHash.toString("hex")).to.be.equal( - expectedP2WSHDeposit.scriptHash - ) + context("when witness option is false", () => { + let scriptHash: Buffer + + beforeEach(async () => { + scriptHash = await DepositScript.fromReceipt( + depositFixture.receipt, + false + ).getHash() + }) + + it("should return proper non-witness script hash", async () => { + // The plain-text script is in the expectedScript property of the fixture. + // The hash of this script should correspond to the OP_HASH160 opcode + // which applies SHA-256 and then RIPEMD-160 on the input. The hash + // can be verified with the following command: + // echo -n $SCRIPT | xxd -r -p | openssl dgst -sha256 -binary | openssl dgst -rmd160 + expect(scriptHash.toString("hex")).to.be.equal( + depositFixture.expectedP2SHData.scriptHash + ) + }) }) }) - context("when witness option is false", () => { - let scriptHash: Buffer + context("when deposit has optional extra data", () => { + context("when witness option is true", () => { + let scriptHash: Buffer - beforeEach(async () => { - scriptHash = await DepositScript.fromReceipt( - depositReceipt, - false - ).getHash() + beforeEach(async () => { + scriptHash = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + true + ).getHash() + }) + + it("should return proper witness script hash", async () => { + // The plain-text script is in the expectedScript property of the fixture. + // The hash of this script should correspond to the OP_SHA256 opcode + // which applies SHA-256 on the input. The hash can be verified with + // the following command: + // echo -n $SCRIPT | xxd -r -p | openssl dgst -sha256 + expect(scriptHash.toString("hex")).to.be.equal( + depositWithExtraDataFixture.expectedP2WSHData.scriptHash + ) + }) }) - it("should return proper non-witness script hash", async () => { - // The script for given deposit should be the same as in - // assembleDepositScript test scenario i.e. expectedDepositScript. - // The hash of this script should correspond to the OP_HASH160 opcode - // which applies SHA-256 and then RIPEMD-160 on the input. In this case - // the hash is expectedP2SHDeposit.scriptHash and it can be verified - // with the following command: - // echo -n $SCRIPT | xxd -r -p | openssl dgst -sha256 -binary | openssl dgst -rmd160 - expect(scriptHash.toString("hex")).to.be.equal( - expectedP2SHDeposit.scriptHash - ) + context("when witness option is false", () => { + let scriptHash: Buffer + + beforeEach(async () => { + scriptHash = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + false + ).getHash() + }) + + it("should return proper non-witness script hash", async () => { + // The plain-text script is in the expectedScript property of the fixture. + // The hash of this script should correspond to the OP_HASH160 opcode + // which applies SHA-256 and then RIPEMD-160 on the input. The hash + // can be verified with the following command: + // echo -n $SCRIPT | xxd -r -p | openssl dgst -sha256 -binary | openssl dgst -rmd160 + expect(scriptHash.toString("hex")).to.be.equal( + depositWithExtraDataFixture.expectedP2SHData.scriptHash + ) + }) }) }) }) @@ -604,74 +1069,166 @@ describe("Deposits", () => { describe("deriveAddress", () => { let address: string - context("when network is mainnet", () => { - context("when witness option is true", () => { - beforeEach(async () => { - address = await DepositScript.fromReceipt( - depositReceipt, - true - ).deriveAddress(BitcoinNetwork.Mainnet) + context("when deposit does not have optional extra data", () => { + context("when network is mainnet", () => { + context("when witness option is true", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositFixture.receipt, + true + ).deriveAddress(BitcoinNetwork.Mainnet) + }) + + it("should return proper address with prefix bc1", async () => { + // Address is created using the script hash held by the + // expectedP2WSHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2WSH (Bech32) address prefix for mainnet is bc1. + expect(address).to.be.equal( + depositFixture.expectedP2WSHData.mainnetAddress + ) + }) }) - it("should return proper address with prefix bc1", async () => { - // Address is created from same script hash as presented in the witness - // DepositScript.getHash scenario i.e. expectedP2WSHDeposit.scriptHash. - // According to https://en.bitcoin.it/wiki/List_of_address_prefixes - // the P2WSH (Bech32) address prefix for mainnet is bc1. - expect(address).to.be.equal(expectedP2WSHDeposit.mainnetAddress) + context("when witness option is false", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositFixture.receipt, + false + ).deriveAddress(BitcoinNetwork.Mainnet) + }) + + it("should return proper address with prefix 3", async () => { + // Address is created using the script hash held by the + // expectedP2SHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2SH address prefix for mainnet is 3. + expect(address).to.be.equal( + depositFixture.expectedP2SHData.mainnetAddress + ) + }) }) }) - context("when witness option is false", () => { - beforeEach(async () => { - address = await DepositScript.fromReceipt( - depositReceipt, - false - ).deriveAddress(BitcoinNetwork.Mainnet) + context("when network is testnet", () => { + context("when witness option is true", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositFixture.receipt, + true + ).deriveAddress(BitcoinNetwork.Testnet) + }) + + it("should return proper address with prefix tb1", async () => { + // Address is created using the script hash held by the + // expectedP2WSHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2WSH (Bech32) address prefix for testnet is tb1. + expect(address).to.be.equal( + depositFixture.expectedP2WSHData.testnetAddress + ) + }) }) - it("should return proper address with prefix 3", async () => { - // Address is created from same script hash as presented in the non-witness - // DepositScript.getHash scenario i.e. expectedP2SHDeposit.scriptHash. - // According to https://en.bitcoin.it/wiki/List_of_address_prefixes - // the P2SH address prefix for mainnet is 3. - expect(address).to.be.equal(expectedP2SHDeposit.mainnetAddress) + context("when witness option is false", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositFixture.receipt, + false + ).deriveAddress(BitcoinNetwork.Testnet) + }) + + it("should return proper address with prefix 2", async () => { + // Address is created using the script hash held by the + // expectedP2SHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2SH address prefix for testnet is 2. + expect(address).to.be.equal( + depositFixture.expectedP2SHData.testnetAddress + ) + }) }) }) }) - context("when network is testnet", () => { - context("when witness option is true", () => { - beforeEach(async () => { - address = await DepositScript.fromReceipt( - depositReceipt, - true - ).deriveAddress(BitcoinNetwork.Testnet) + context("when deposit has optional extra data", () => { + context("when network is mainnet", () => { + context("when witness option is true", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + true + ).deriveAddress(BitcoinNetwork.Mainnet) + }) + + it("should return proper address with prefix bc1", async () => { + // Address is created using the script hash held by the + // expectedP2WSHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2WSH (Bech32) address prefix for mainnet is bc1. + expect(address).to.be.equal( + depositWithExtraDataFixture.expectedP2WSHData.mainnetAddress + ) + }) }) - it("should return proper address with prefix tb1", async () => { - // Address is created from same script hash as presented in the witness - // DepositScript.getHash scenario i.e. expectedP2WSHDeposit.scriptHash. - // According to https://en.bitcoin.it/wiki/List_of_address_prefixes - // the P2WSH (Bech32) address prefix for testnet is tb1. - expect(address).to.be.equal(expectedP2WSHDeposit.testnetAddress) + context("when witness option is false", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + false + ).deriveAddress(BitcoinNetwork.Mainnet) + }) + + it("should return proper address with prefix 3", async () => { + // Address is created using the script hash held by the + // expectedP2SHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2SH address prefix for mainnet is 3. + expect(address).to.be.equal( + depositWithExtraDataFixture.expectedP2SHData.mainnetAddress + ) + }) }) }) - context("when witness option is false", () => { - beforeEach(async () => { - address = await DepositScript.fromReceipt( - depositReceipt, - false - ).deriveAddress(BitcoinNetwork.Testnet) + context("when network is testnet", () => { + context("when witness option is true", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + true + ).deriveAddress(BitcoinNetwork.Testnet) + }) + + it("should return proper address with prefix tb1", async () => { + // Address is created using the script hash held by the + // expectedP2WSHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2WSH (Bech32) address prefix for testnet is tb1. + expect(address).to.be.equal( + depositWithExtraDataFixture.expectedP2WSHData.testnetAddress + ) + }) }) - it("should return proper address with prefix 2", async () => { - // Address is created from same script hash as presented in the witness - // DepositScript.getHash scenario i.e. expectedP2SHDeposit.scriptHash. - // According to https://en.bitcoin.it/wiki/List_of_address_prefixes - // the P2SH address prefix for testnet is 2. - expect(address).to.be.equal(expectedP2SHDeposit.testnetAddress) + context("when witness option is false", () => { + beforeEach(async () => { + address = await DepositScript.fromReceipt( + depositWithExtraDataFixture.receipt, + false + ).deriveAddress(BitcoinNetwork.Testnet) + }) + + it("should return proper address with prefix 2", async () => { + // Address is created using the script hash held by the + // expectedP2SHData.scriptHash property of the fixture. + // According to https://en.bitcoin.it/wiki/List_of_address_prefixes, + // the P2SH address prefix for testnet is 2. + expect(address).to.be.equal( + depositWithExtraDataFixture.expectedP2SHData.testnetAddress + ) + }) }) }) }) @@ -682,20 +1239,26 @@ describe("Deposits", () => { describe("getBitcoinAddress", () => { const testData = [ { + fixture: depositFixture, + network: BitcoinNetwork.Mainnet, + }, + { + fixture: depositFixture, + network: BitcoinNetwork.Testnet, + }, + { + fixture: depositWithExtraDataFixture, network: BitcoinNetwork.Mainnet, - expectedAddress: - "bc1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhsdxuv4m", }, { + fixture: depositWithExtraDataFixture, network: BitcoinNetwork.Testnet, - expectedAddress: - "tb1qma629cu92skg0t86lftyaf9uflzwhp7jk63h6mpmv3ezh6puvdhs6w2r05", }, ] let bitcoinAddress: string - testData.forEach(({ network, expectedAddress }) => { + testData.forEach(({ fixture, network }) => { context(`when network is ${network}`, () => { beforeEach(async () => { const bitcoinClient = new MockBitcoinClient() @@ -703,7 +1266,7 @@ describe("Deposits", () => { const tbtcContracts = new MockTBTCContracts() const deposit = await Deposit.fromReceipt( - depositReceipt, + fixture.receipt, tbtcContracts, bitcoinClient ) @@ -712,6 +1275,18 @@ describe("Deposits", () => { }) it("should return correct address", () => { + const { mainnetAddress, testnetAddress } = fixture.expectedP2WSHData + + let expectedAddress + switch (network) { + case BitcoinNetwork.Mainnet: + expectedAddress = mainnetAddress + break + case BitcoinNetwork.Testnet: + expectedAddress = testnetAddress + break + } + expect(bitcoinAddress).to.be.equal(expectedAddress) }) }) @@ -729,7 +1304,7 @@ describe("Deposits", () => { tbtcContracts = new MockTBTCContracts() deposit = await Deposit.fromReceipt( - depositReceipt, + depositFixture.receipt, tbtcContracts, bitcoinClient ) @@ -798,8 +1373,8 @@ describe("Deposits", () => { }) describe("initiateMinting", () => { - describe("auto funding outpoint detection mode", () => { - describe("when no funding UTXOs found", () => { + context("auto funding outpoint detection mode", () => { + context("when no funding UTXOs found", () => { let deposit: Deposit beforeEach(async () => { @@ -807,7 +1382,7 @@ describe("Deposits", () => { const bitcoinClient = new MockBitcoinClient() deposit = await Deposit.fromReceipt( - depositReceipt, + depositFixture.receipt, tbtcContracts, bitcoinClient ) @@ -820,16 +1395,11 @@ describe("Deposits", () => { }) }) - describe("when funding UTXOs found", () => { - let transaction: BitcoinRawTx - let depositUtxo: BitcoinUtxo - let tbtcContracts: MockTBTCContracts - let bitcoinClient: MockBitcoinClient - - beforeEach(async () => { + context("when funding UTXOs found", () => { + const initiateMinting = async (depositorProxy?: DepositorProxy) => { const fee = BigNumber.from(1520) const depositFunding = DepositFunding.fromScript( - DepositScript.fromReceipt(depositReceipt) + DepositScript.fromReceipt(depositFixture.receipt) ) // Create a deposit transaction. const result = await depositFunding.assembleTransaction( @@ -839,18 +1409,19 @@ describe("Deposits", () => { fee, testnetPrivateKey ) - transaction = result.rawTransaction - depositUtxo = result.depositUtxo + const transaction: BitcoinRawTx = result.rawTransaction + const depositUtxo: BitcoinUtxo = result.depositUtxo // Initialize the mock Bridge and TBTC contracts. - bitcoinClient = new MockBitcoinClient() - tbtcContracts = new MockTBTCContracts() + const bitcoinClient: MockBitcoinClient = new MockBitcoinClient() + const tbtcContracts: MockTBTCContracts = new MockTBTCContracts() // Create the deposit. const deposit = await Deposit.fromReceipt( - depositReceipt, + depositFixture.receipt, tbtcContracts, - bitcoinClient + bitcoinClient, + depositorProxy ) // Initialize the mock Bitcoin client to return the given deposit @@ -871,32 +1442,76 @@ describe("Deposits", () => { bitcoinClient.rawTransactions = rawTransactions await deposit.initiateMinting() + + return { + transaction, + tbtcContracts, + } + } + + context("when deposit does not use a depositor proxy", () => { + let transaction: BitcoinRawTx + let tbtcContracts: MockTBTCContracts + + beforeEach(async () => { + ;({ transaction, tbtcContracts } = await initiateMinting()) + }) + + it("should reveal the deposit to the Bridge", () => { + expect(tbtcContracts.bridge.revealDepositLog.length).to.be.equal( + 1 + ) + const revealDepositLogEntry = + tbtcContracts.bridge.revealDepositLog[0] + expect(revealDepositLogEntry.depositTx).to.be.eql( + extractBitcoinRawTxVectors(transaction) + ) + expect(revealDepositLogEntry.depositOutputIndex).to.be.equal(0) + expect(revealDepositLogEntry.deposit).to.be.eql( + depositFixture.receipt + ) + }) }) - it("should reveal the deposit to the Bridge", () => { - expect(tbtcContracts.bridge.revealDepositLog.length).to.be.equal(1) - const revealDepositLogEntry = - tbtcContracts.bridge.revealDepositLog[0] - expect(revealDepositLogEntry.depositTx).to.be.eql( - extractBitcoinRawTxVectors(transaction) - ) - expect(revealDepositLogEntry.depositOutputIndex).to.be.equal(0) - expect(revealDepositLogEntry.deposit).to.be.eql(depositReceipt) + context("when deposit uses a depositor proxy", () => { + let transaction: BitcoinRawTx + let tbtcContracts: MockTBTCContracts + let depositorProxy: MockDepositorProxy + + beforeEach(async () => { + depositorProxy = new MockDepositorProxy() + ;({ transaction, tbtcContracts } = await initiateMinting( + depositorProxy + )) + }) + + it("should not reveal the deposit to the Bridge", () => { + expect(tbtcContracts.bridge.revealDepositLog.length).to.be.equal( + 0 + ) + }) + + it("should reveal the deposit to the DepositorProxy", () => { + expect(depositorProxy.revealDepositLog.length).to.be.equal(1) + const revealDepositLogEntry = depositorProxy.revealDepositLog[0] + expect(revealDepositLogEntry.depositTx).to.be.eql( + extractBitcoinRawTxVectors(transaction) + ) + expect(revealDepositLogEntry.depositOutputIndex).to.be.equal(0) + expect(revealDepositLogEntry.deposit).to.be.eql( + depositFixture.receipt + ) + }) }) }) }) - describe("manual funding outpoint provision mode", () => { - let transaction: BitcoinRawTx - let depositUtxo: BitcoinUtxo - let tbtcContracts: MockTBTCContracts - let bitcoinClient: MockBitcoinClient - - beforeEach(async () => { + context("manual funding outpoint provision mode", () => { + const initiateMinting = async (depositorProxy?: DepositorProxy) => { const fee = BigNumber.from(1520) const depositFunding = DepositFunding.fromScript( - DepositScript.fromReceipt(depositReceipt) + DepositScript.fromReceipt(depositFixture.receipt) ) // Create a deposit transaction. @@ -908,12 +1523,12 @@ describe("Deposits", () => { testnetPrivateKey ) - transaction = result.rawTransaction - depositUtxo = result.depositUtxo + const transaction: BitcoinRawTx = result.rawTransaction + const depositUtxo: BitcoinUtxo = result.depositUtxo // Initialize the mock Bitcoin client to return the raw transaction // data for the given deposit UTXO. - bitcoinClient = new MockBitcoinClient() + const bitcoinClient: MockBitcoinClient = new MockBitcoinClient() const rawTransactions = new Map() rawTransactions.set( depositUtxo.transactionHash.toString(), @@ -922,26 +1537,74 @@ describe("Deposits", () => { bitcoinClient.rawTransactions = rawTransactions // Initialize the mock Bridge. - tbtcContracts = new MockTBTCContracts() + const tbtcContracts: MockTBTCContracts = new MockTBTCContracts() await ( await Deposit.fromReceipt( - depositReceipt, + depositFixture.receipt, tbtcContracts, - bitcoinClient + bitcoinClient, + depositorProxy ) ).initiateMinting(depositUtxo) + + return { + transaction, + tbtcContracts, + } + } + + context("when deposit does not use a depositor proxy", () => { + let transaction: BitcoinRawTx + let tbtcContracts: MockTBTCContracts + + beforeEach(async () => { + ;({ transaction, tbtcContracts } = await initiateMinting()) + }) + + it("should reveal the deposit to the Bridge", () => { + expect(tbtcContracts.bridge.revealDepositLog.length).to.be.equal(1) + + const revealDepositLogEntry = + tbtcContracts.bridge.revealDepositLog[0] + expect(revealDepositLogEntry.depositTx).to.be.eql( + extractBitcoinRawTxVectors(transaction) + ) + expect(revealDepositLogEntry.depositOutputIndex).to.be.equal(0) + expect(revealDepositLogEntry.deposit).to.be.eql( + depositFixture.receipt + ) + }) }) - it("should reveal the deposit to the Bridge", () => { - expect(tbtcContracts.bridge.revealDepositLog.length).to.be.equal(1) + context("when deposit uses a depositor proxy", () => { + let transaction: BitcoinRawTx + let tbtcContracts: MockTBTCContracts + let depositorProxy: MockDepositorProxy - const revealDepositLogEntry = tbtcContracts.bridge.revealDepositLog[0] - expect(revealDepositLogEntry.depositTx).to.be.eql( - extractBitcoinRawTxVectors(transaction) - ) - expect(revealDepositLogEntry.depositOutputIndex).to.be.equal(0) - expect(revealDepositLogEntry.deposit).to.be.eql(depositReceipt) + beforeEach(async () => { + depositorProxy = new MockDepositorProxy() + ;({ transaction, tbtcContracts } = await initiateMinting( + depositorProxy + )) + }) + + it("should not reveal the deposit to the Bridge", () => { + expect(tbtcContracts.bridge.revealDepositLog.length).to.be.equal(0) + }) + + it("should reveal the deposit to the DepositorProxy", () => { + expect(depositorProxy.revealDepositLog.length).to.be.equal(1) + + const revealDepositLogEntry = depositorProxy.revealDepositLog[0] + expect(revealDepositLogEntry.depositTx).to.be.eql( + extractBitcoinRawTxVectors(transaction) + ) + expect(revealDepositLogEntry.depositOutputIndex).to.be.equal(0) + expect(revealDepositLogEntry.deposit).to.be.eql( + depositFixture.receipt + ) + }) }) }) }) @@ -1007,34 +1670,216 @@ describe("Deposits", () => { }) context("when recovery address is correct", () => { + const assertCommonDepositProperties = (receipt: DepositReceipt) => { + expect(receipt.depositor).to.be.equal(depositor) + + expect(receipt.walletPublicKeyHash).to.be.deep.equal( + Hex.from("8db50eb52063ea9d98b3eac91489a90f738986f6") + ) + + // Expect the refund locktime to be in the future. + const receiptTimestamp = BigNumber.from( + receipt.refundLocktime.reverse().toPrefixedString() + ).toNumber() + const currentTimestamp = Math.floor(new Date().getTime() / 1000) + expect(receiptTimestamp).to.be.greaterThan(currentTimestamp) + + // Expect blinding factor to be set and 8-byte long. + expect(receipt.blindingFactor).not.to.be.undefined + expect(receipt.blindingFactor.toBuffer().length).to.be.equal(8) + } + + context("when optional extra data is not provided", () => { + context("when recovery address is P2PKH", () => { + let deposit: Deposit + + beforeEach(async () => { + deposit = await depositService.initiateDeposit( + "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc" + ) + }) + + it("should initiate deposit correctly", async () => { + // Inspect the deposit object by looking at its receipt. + const receipt = deposit.getReceipt() + + assertCommonDepositProperties(receipt) + + expect(receipt.refundPublicKeyHash).to.be.deep.equal( + Hex.from("2cd680318747b720d67bf4246eb7403b476adb34") + ) + expect(receipt.extraData).to.be.undefined + }) + }) + + context("when recovery address is P2WPKH", () => { + let deposit: Deposit + + beforeEach(async () => { + deposit = await depositService.initiateDeposit( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx" + ) + }) + + it("should initiate deposit correctly", async () => { + // Inspect the deposit object by looking at its receipt. + const receipt = deposit.getReceipt() + + assertCommonDepositProperties(receipt) + + expect(receipt.refundPublicKeyHash).to.be.deep.equal( + Hex.from("e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0") + ) + expect(receipt.extraData).to.be.undefined + }) + }) + }) + + context("when optional extra data is provided", () => { + context("when extra data is not 32-byte", () => { + it("should throw", async () => { + await expect( + depositService.initiateDeposit( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + Hex.from("11") + ) + ).to.be.rejectedWith("Extra data is not 32-byte") + }) + }) + + context("when extra data is 32-byte but all-zero", () => { + it("should throw", async () => { + await expect( + depositService.initiateDeposit( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + Hex.from( + "0000000000000000000000000000000000000000000000000000000000000000" + ) + ) + ).to.be.rejectedWith("Extra data contains only zero bytes") + }) + }) + + context("when extra data is 32-byte and non-zero", () => { + let deposit: Deposit + + beforeEach(async () => { + deposit = await depositService.initiateDeposit( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + Hex.from( + "1111111111111111222222222222222211111111111111112222222222222222" + ) + ) + }) + + it("should initiate deposit correctly", async () => { + // Inspect the deposit object by looking at its receipt. + const receipt = deposit.getReceipt() + + assertCommonDepositProperties(receipt) + + expect(receipt.refundPublicKeyHash).to.be.deep.equal( + Hex.from("e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0") + ) + expect(receipt.extraData).to.be.eql( + Hex.from( + "1111111111111111222222222222222211111111111111112222222222222222" + ) + ) + }) + }) + }) + }) + }) + }) + }) + + describe("initiateDepositWithProxy", () => { + const bitcoinClient = new MockBitcoinClient() + const tbtcContracts = new MockTBTCContracts() + const depositorProxy = new MockDepositorProxy() + let depositService: DepositsService + + beforeEach(async () => { + depositService = new DepositsService(tbtcContracts, bitcoinClient) + }) + + context("when active wallet is not set", () => { + it("should throw", async () => { + await expect( + depositService.initiateDepositWithProxy( + "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc", + depositorProxy + ) + ).to.be.rejectedWith("Could not get active wallet public key") + }) + }) + + context("when active wallet is set", () => { + beforeEach(async () => { + tbtcContracts.bridge.setActiveWalletPublicKey( + Hex.from( + "03989d253b17a6a0f41838b84ff0d20e8898f9d7b1a98f2564da4cc29dcf8581d9" + ) + ) + }) + + context("when recovery address is incorrect", () => { + it("should throw", async () => { + await expect( + depositService.initiateDepositWithProxy( + "2N5WZpig3vgpSdjSherS2Lv7GnPuxCvkQjT", // p2sh address + depositorProxy + ) + ).to.be.rejectedWith( + "Bitcoin recovery address must be P2PKH or P2WPKH" + ) + }) + }) + + context("when recovery address is correct", () => { + const assertCommonDepositProperties = (receipt: DepositReceipt) => { + expect(receipt.depositor).to.be.deep.equal( + depositorProxy.getChainIdentifier() + ) + + expect(receipt.walletPublicKeyHash).to.be.deep.equal( + Hex.from("8db50eb52063ea9d98b3eac91489a90f738986f6") + ) + + // Expect the refund locktime to be in the future. + const receiptTimestamp = BigNumber.from( + receipt.refundLocktime.reverse().toPrefixedString() + ).toNumber() + const currentTimestamp = Math.floor(new Date().getTime() / 1000) + expect(receiptTimestamp).to.be.greaterThan(currentTimestamp) + + // Expect blinding factor to be set and 8-byte long. + expect(receipt.blindingFactor).not.to.be.undefined + expect(receipt.blindingFactor.toBuffer().length).to.be.equal(8) + } + + context("when optional extra data is not provided", () => { context("when recovery address is P2PKH", () => { let deposit: Deposit beforeEach(async () => { - deposit = await depositService.initiateDeposit( - "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc" + deposit = await depositService.initiateDepositWithProxy( + "mjc2zGWypwpNyDi4ZxGbBNnUA84bfgiwYc", + depositorProxy ) }) it("should initiate deposit correctly", async () => { // Inspect the deposit object by looking at its receipt. const receipt = deposit.getReceipt() - expect(receipt.depositor).to.be.equal(depositor) + + assertCommonDepositProperties(receipt) + expect(receipt.refundPublicKeyHash).to.be.deep.equal( Hex.from("2cd680318747b720d67bf4246eb7403b476adb34") ) - expect(receipt.walletPublicKeyHash).to.be.deep.equal( - Hex.from("8db50eb52063ea9d98b3eac91489a90f738986f6") - ) - // Expect the refund locktime to be in the future. - const receiptTimestamp = BigNumber.from( - receipt.refundLocktime.reverse().toPrefixedString() - ).toNumber() - const currentTimestamp = Math.floor(new Date().getTime() / 1000) - expect(receiptTimestamp).to.be.greaterThan(currentTimestamp) - // Expect blinding factor to be set and 8-byte long. - expect(receipt.blindingFactor).not.to.be.undefined - expect(receipt.blindingFactor.toBuffer().length).to.be.equal(8) + expect(receipt.extraData).to.be.undefined }) }) @@ -1042,30 +1887,80 @@ describe("Deposits", () => { let deposit: Deposit beforeEach(async () => { - deposit = await depositService.initiateDeposit( - "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx" + deposit = await depositService.initiateDepositWithProxy( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + depositorProxy + ) + }) + + it("should initiate deposit correctly", async () => { + // Inspect the deposit object by looking at its receipt. + const receipt = deposit.getReceipt() + + assertCommonDepositProperties(receipt) + + expect(receipt.refundPublicKeyHash).to.be.deep.equal( + Hex.from("e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0") + ) + expect(receipt.extraData).to.be.undefined + }) + }) + }) + + context("when optional extra data is provided", () => { + context("when extra data is not 32-byte", () => { + it("should throw", async () => { + await expect( + depositService.initiateDepositWithProxy( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + depositorProxy, + Hex.from("11") + ) + ).to.be.rejectedWith("Extra data is not 32-byte") + }) + }) + + context("when extra data is 32-byte but all-zero", () => { + it("should throw", async () => { + await expect( + depositService.initiateDepositWithProxy( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + depositorProxy, + Hex.from( + "0000000000000000000000000000000000000000000000000000000000000000" + ) + ) + ).to.be.rejectedWith("Extra data contains only zero bytes") + }) + }) + + context("when extra data is 32-byte and non-zero", () => { + let deposit: Deposit + + beforeEach(async () => { + deposit = await depositService.initiateDepositWithProxy( + "tb1qumuaw3exkxdhtut0u85latkqfz4ylgwstkdzsx", + depositorProxy, + Hex.from( + "1111111111111111222222222222222211111111111111112222222222222222" + ) ) }) it("should initiate deposit correctly", async () => { // Inspect the deposit object by looking at its receipt. const receipt = deposit.getReceipt() - expect(receipt.depositor).to.be.equal(depositor) + + assertCommonDepositProperties(receipt) + expect(receipt.refundPublicKeyHash).to.be.deep.equal( Hex.from("e6f9d74726b19b75f16fe1e9feaec048aa4fa1d0") ) - expect(receipt.walletPublicKeyHash).to.be.deep.equal( - Hex.from("8db50eb52063ea9d98b3eac91489a90f738986f6") + expect(receipt.extraData).to.be.eql( + Hex.from( + "1111111111111111222222222222222211111111111111112222222222222222" + ) ) - // Expect the refund locktime to be in the future. - const receiptTimestamp = BigNumber.from( - receipt.refundLocktime.reverse().toPrefixedString() - ).toNumber() - const currentTimestamp = Math.floor(new Date().getTime() / 1000) - expect(receiptTimestamp).to.be.greaterThan(currentTimestamp) - // Expect blinding factor to be set and 8-byte long. - expect(receipt.blindingFactor).not.to.be.undefined - expect(receipt.blindingFactor.toBuffer().length).to.be.equal(8) }) }) }) diff --git a/typescript/test/utils/mock-depositor-proxy.ts b/typescript/test/utils/mock-depositor-proxy.ts new file mode 100644 index 000000000..4744f1e97 --- /dev/null +++ b/typescript/test/utils/mock-depositor-proxy.ts @@ -0,0 +1,43 @@ +import { + BitcoinRawTxVectors, + ChainIdentifier, + DepositorProxy, + DepositReceipt, + EthereumAddress, + Hex, +} from "../../src" + +interface RevealDepositLogEntry { + depositTx: BitcoinRawTxVectors + depositOutputIndex: number + deposit: DepositReceipt +} + +export class MockDepositorProxy implements DepositorProxy { + private _revealDepositLog: RevealDepositLogEntry[] = [] + + get revealDepositLog(): RevealDepositLogEntry[] { + return this._revealDepositLog + } + + getChainIdentifier(): ChainIdentifier { + return EthereumAddress.from("0xEdA7bE2D82566ce2546b150447b5cb0E4320a1B2") + } + + revealDeposit( + depositTx: BitcoinRawTxVectors, + depositOutputIndex: number, + deposit: DepositReceipt, + vault?: ChainIdentifier + ): Promise { + this._revealDepositLog.push({ depositTx, depositOutputIndex, deposit }) + return new Promise((resolve, _) => { + // random transaction hash + resolve( + Hex.from( + "3f952bdc206bf51bb745b967cb7166149becada878d3191ffe341155ebcd4883" + ) + ) + }) + } +}