diff --git a/examples/cli/package.json b/examples/cli/package.json index 47623e2e..1f84da04 100644 --- a/examples/cli/package.json +++ b/examples/cli/package.json @@ -1,8 +1,8 @@ { "name": "@cere-ddc-sdk/cli-examples", - "version": "2.15.0", + "version": "2.16.0", "private": true, "dependencies": { - "@cere-ddc-sdk/cli": "2.15.0" + "@cere-ddc-sdk/cli": "2.16.0" } } diff --git a/examples/node/package.json b/examples/node/package.json index c3559480..e2c2e8fe 100644 --- a/examples/node/package.json +++ b/examples/node/package.json @@ -1,6 +1,6 @@ { "name": "@cere-ddc-sdk/node-examples", - "version": "2.15.0", + "version": "2.16.0", "private": true, "type": "module", "scripts": { @@ -14,8 +14,8 @@ "example:8": "ts-node --esm ./8-sudo-batch-remove-buckets/index.ts" }, "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc-client": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc-client": "2.16.0", "@types/json-bigint": "^1.0.4", "json-bigint": "^1.0.0", "ts-node": "^10.9.2" diff --git a/lerna.json b/lerna.json index e884c578..555c2087 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "2.15.0", + "version": "2.16.0", "command": { "publish": { "directory": "{workspaceRoot}/{projectRoot}/package" diff --git a/package-lock.json b/package-lock.json index cca0b7e1..2feeb487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,17 +38,17 @@ }, "examples/cli": { "name": "@cere-ddc-sdk/cli-examples", - "version": "2.15.0", + "version": "2.16.0", "dependencies": { - "@cere-ddc-sdk/cli": "2.15.0" + "@cere-ddc-sdk/cli": "2.16.0" } }, "examples/node": { "name": "@cere-ddc-sdk/node-examples", - "version": "2.15.0", + "version": "2.16.0", "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc-client": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc-client": "2.16.0", "@types/json-bigint": "^1.0.4", "json-bigint": "^1.0.0", "ts-node": "^10.9.2" @@ -23143,7 +23143,7 @@ }, "packages/blockchain": { "name": "@cere-ddc-sdk/blockchain", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { "@cere/embed-wallet-inject": "^0.20.1", @@ -23477,7 +23477,7 @@ }, "packages/changelog-preset": { "name": "@cere-ddc-sdk/conventional-changelog-changelog-preset", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { "conventional-changelog-conventionalcommits": "^7.0.2" @@ -23485,11 +23485,11 @@ }, "packages/cli": { "name": "@cere-ddc-sdk/cli", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { - "@cere-ddc-sdk/ddc": "2.15.0", - "@cere-ddc-sdk/ddc-client": "2.15.0", + "@cere-ddc-sdk/ddc": "2.16.0", + "@cere-ddc-sdk/ddc-client": "2.16.0", "@polkadot/util-crypto": "^13.3.1", "@types/yargs": "^17.0.32", "yargs": "^17.7.2" @@ -23503,10 +23503,10 @@ }, "packages/ddc": { "name": "@cere-ddc-sdk/ddc", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", "@grpc/grpc-js": "^1.9.13", "@protobuf-ts/grpc-transport": "^2.9.3", "@protobuf-ts/runtime": "^2.9.3", @@ -23535,12 +23535,12 @@ }, "packages/ddc-client": { "name": "@cere-ddc-sdk/ddc-client", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc": "2.15.0", - "@cere-ddc-sdk/file-storage": "2.15.0" + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc": "2.16.0", + "@cere-ddc-sdk/file-storage": "2.16.0" } }, "packages/ddc/node_modules/buffer": { @@ -23567,7 +23567,7 @@ }, "packages/eslint-config": { "name": "@cere-ddc-sdk/eslint-config", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { "@typescript-eslint/eslint-plugin": "^6.16.0", @@ -23584,16 +23584,16 @@ }, "packages/file-storage": { "name": "@cere-ddc-sdk/file-storage", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc": "2.15.0" + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc": "2.16.0" } }, "packages/typedoc-config": { "name": "@cere-ddc-sdk/typedoc-config", - "version": "2.15.0", + "version": "2.16.0", "license": "Apache-2.0", "dependencies": { "typedoc-plugin-markdown": "^3.17.1" @@ -23604,10 +23604,10 @@ }, "playground": { "name": "@cere-ddc-sdk/playground", - "version": "2.15.0", + "version": "2.16.0", "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc-client": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc-client": "2.16.0", "@cere/embed-wallet": "^0.20.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", @@ -23631,12 +23631,12 @@ }, "tests": { "name": "@cere-ddc-sdk/tests", - "version": "2.15.0", + "version": "2.16.0", "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc": "2.15.0", - "@cere-ddc-sdk/ddc-client": "2.15.0", - "@cere-ddc-sdk/file-storage": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc": "2.16.0", + "@cere-ddc-sdk/ddc-client": "2.16.0", + "@cere-ddc-sdk/file-storage": "2.16.0", "@types/jest": "^29.5.11", "internal-ip": "6.2.0", "jest": "^29.7.0", diff --git a/packages/blockchain/package.json b/packages/blockchain/package.json index 87468700..e80b5c4f 100644 --- a/packages/blockchain/package.json +++ b/packages/blockchain/package.json @@ -1,6 +1,6 @@ { "name": "@cere-ddc-sdk/blockchain", - "version": "2.15.0", + "version": "2.16.0", "description": "Cere Blockchain client", "type": "module", "main": "dist/index.cjs", diff --git a/packages/blockchain/src/Blockchain.ts b/packages/blockchain/src/Blockchain.ts index 6079ba7f..4656bf3a 100644 --- a/packages/blockchain/src/Blockchain.ts +++ b/packages/blockchain/src/Blockchain.ts @@ -11,6 +11,7 @@ import { DDCClustersPallet } from './DDCClustersPallet'; import { DDCStakingPallet } from './DDCStakingPallet'; import { DDCCustomersPallet } from './DDCCustomersPallet'; import { DDCClustersGovPallet } from './DDCClustersGovPallet'; +import { CustomerDepositContract } from './CustomerDepositContract'; export type SendOptions = Pick, 'nonce' | 'signer'> & { account: AddressOrPair | Signer; @@ -332,6 +333,23 @@ export class Blockchain { const { number } = await this.api.rpc.chain.getHeader(); return number.toNumber(); } + + /** + * Creates an instance of CustomerDepositContract to work with the deposit smart contract. + * + * @param contractAddress - Address of the deployed smart contract customer deposit + * @returns Instance of CustomerDepositContract + * + * @example + * ```typescript + * const contractAddress = '5GBWmgdFAMqm8ZgAHGobqDqX6tjLxJhv53ygjNtaaAn3sjeZ'; + * const contract = blockchain.getCustomerDepositContract(contractAddress); + * const balance = await contract.getBalance('5D5PhZQNJzcJXVBxwJxZcsutjKPqUPydrvpu6HeiBfMae2Qu'); + * ``` + */ + getCustomerDepositContract(contractAddress: string): CustomerDepositContract { + return new CustomerDepositContract(this.api, contractAddress); + } } export type Sendable = SubmittableExtrinsic<'promise'>; diff --git a/packages/blockchain/src/CustomerDepositContract.ts b/packages/blockchain/src/CustomerDepositContract.ts new file mode 100644 index 00000000..a42ab452 --- /dev/null +++ b/packages/blockchain/src/CustomerDepositContract.ts @@ -0,0 +1,193 @@ +import { ApiPromise } from '@polkadot/api'; +import { ContractPromise } from '@polkadot/api-contract'; +import { WeightV2 } from '@polkadot/types/interfaces'; +import { SubmittableExtrinsic } from '@polkadot/api-base/types'; +import { AccountId } from './index'; +import customerDepositAbi from './abi/customer_deposit.json'; + +type Sendable = SubmittableExtrinsic<'promise'>; + +export interface Ledger { + owner: AccountId; + total: bigint; + active: bigint; + unlocking: UnlockChunk[]; +} + +export interface UnlockChunk { + value: bigint; + block: number; +} + +export class CustomerDepositContract { + private readonly apiPromise: ApiPromise; + private readonly contract: ContractPromise; + private readonly defaultGasLimit: WeightV2; + + constructor(apiPromise: ApiPromise, contractAddress: string) { + this.apiPromise = apiPromise; + this.contract = new ContractPromise(apiPromise, customerDepositAbi, contractAddress); + this.defaultGasLimit = apiPromise.registry.createType('WeightV2', { + refTime: 10000000000n, + proofSize: 10000000000n, + }); + } + + private ensureConnected(): void { + if (!this.apiPromise.isConnected) { + throw new Error('API must be connected to a chain before using CustomerDepositContract methods'); + } + } + + private toBigIntSafe(value: string | number | bigint): bigint { + if (typeof value === 'string') { + value = value.replace(/[^\d-]/g, ''); + } + return BigInt(value); + } + + /** + * Gets the client's balance in the DDC cluster. + * + * @param owner - The address of the account owner + * @returns Promise that resolves to balance information or null if the balance is not found + */ + async getBalance(owner: AccountId): Promise { + this.ensureConnected(); + + try { + const { result, output } = await this.contract.query['ddcBalancesFetcher::getBalance']( + owner, + { gasLimit: this.defaultGasLimit }, + owner, + ); + + if (!result.isOk || !output) { + return null; + } + + const humanOutput = (output as any).toHuman(); + + if (humanOutput.Err || !humanOutput.Ok) { + return null; + } + + const optionLedger = humanOutput.Ok; + if (!optionLedger) { + console.log('No ledger data found (Option::None)'); + return null; + } + + const ledgerData = optionLedger; + + return { + owner: ledgerData.owner || owner, + total: this.toBigIntSafe(ledgerData.total), + active: this.toBigIntSafe(ledgerData.active), + unlocking: (ledgerData.unlocking || []).map((chunk: any) => ({ + value: this.toBigIntSafe(chunk.value), + block: Number(chunk.block) || 0, + })), + }; + } catch (error) { + console.error('Error getting balance:', error); + return null; + } + } + + /** + * Tops up balance the deposit balance on behalf of the owner. + * + * @param value - Amount to deposit + * @returns Transaction to execute + * + * @example + * ```typescript + * const tx = contract.deposit(100n); + * const result = await blockchain.send(tx, { account: signer }); + * ``` + */ + deposit(value: bigint): Sendable { + this.ensureConnected(); + + const tx = this.contract.tx['ddcBalancesDepositor::deposit']({ + gasLimit: this.defaultGasLimit, + storageDepositLimit: null, + value, + }); + return tx as unknown as Sendable; + } + + /** + * Tops up the deposit balance for a specific owner on behalf of the faucet. + * + * @param owner - Account owner address to top up + * @param value - Amount to deposit + * @returns Transaction to execute + * + * @example + * ```typescript + * const tx = contract.depositFor('5D5PhZQNJzcJXVBxwJxZcsutjKPqUPydrvpu6HeiBfMae2Qu', 100n); + * const result = await blockchain.send(tx, { account: signer }); + * ``` + */ + depositFor(owner: AccountId, value: bigint): Sendable { + this.ensureConnected(); + + const tx = this.contract.tx['ddcBalancesDepositor::depositFor']( + { + gasLimit: this.defaultGasLimit, + storageDepositLimit: null, + value, + }, + owner, + ); + return tx as unknown as Sendable; + } + + /** + * Initiates unlocking of the deposit balance on behalf of the owner. + * + * @param value - Amount to unlock + * @returns Transaction to execute + * + * @example + * ```typescript + * const tx = contract.unlockDeposit(50n); + * const result = await blockchain.send(tx, { account: signer }); + * ``` + */ + unlockDeposit(value: bigint): Sendable { + this.ensureConnected(); + + const tx = this.contract.tx['ddcBalancesDepositor::unlockDeposit']( + { + gasLimit: this.defaultGasLimit, + storageDepositLimit: null, + }, + value, + ); + return tx as unknown as Sendable; + } + + /** + * Outputs the unlocked deposit balance on behalf of the owner. + * + * @returns The transaction to execute + * + * @example + * ```typescript + * const tx = contract.withdrawUnlocked(); + * const result = await blockchain.send(tx, { account: signer }); + * ``` + */ + withdrawUnlocked(): Sendable { + this.ensureConnected(); + + const tx = this.contract.tx['ddcBalancesDepositor::withdrawUnlocked']({ + gasLimit: this.defaultGasLimit, + storageDepositLimit: null, + }); + return tx as unknown as Sendable; + } +} diff --git a/packages/blockchain/src/abi/customer_deposit.json b/packages/blockchain/src/abi/customer_deposit.json new file mode 100644 index 00000000..318e5496 --- /dev/null +++ b/packages/blockchain/src/abi/customer_deposit.json @@ -0,0 +1,1238 @@ +{ + "source": { + "hash": "0xbbbf3041f70477a87350a2ffedb2bb9c065d10b11c5e9348816e8bc039e1ed6c", + "language": "ink! 5.1.1", + "compiler": "rustc 1.82.0", + "build_info": { + "build_mode": "Release", + "cargo_contract_version": "4.1.3", + "rust_toolchain": "stable-aarch64-apple-darwin", + "wasm_opt_settings": { + "keep_debug_symbols": false, + "optimization_passes": "Z" + } + } + }, + "contract": { + "name": "customer-deposit", + "version": "5.1.0", + "authors": ["Use Ink "] + }, + "image": null, + "spec": { + "constructors": [ + { + "args": [ + { + "label": "cluster_id", + "type": { + "displayName": ["ClusterId20"], + "type": 18 + } + }, + { + "label": "unlock_delay_blocks", + "type": { + "displayName": ["u32"], + "type": 1 + } + } + ], + "default": false, + "docs": [], + "label": "new", + "payable": false, + "returnType": { + "displayName": ["ink_primitives", "ConstructorResult"], + "type": 19 + }, + "selector": "0x9bae9d5e" + } + ], + "docs": [], + "environment": { + "accountId": { + "displayName": ["AccountId"], + "type": 6 + }, + "balance": { + "displayName": ["Balance"], + "type": 2 + }, + "blockNumber": { + "displayName": ["BlockNumber"], + "type": 1 + }, + "chainExtension": { + "displayName": ["ChainExtension"], + "type": 37 + }, + "hash": { + "displayName": ["Hash"], + "type": 36 + }, + "maxEventTopics": 4, + "staticBufferSize": 16384, + "timestamp": { + "displayName": ["Timestamp"], + "type": 14 + } + }, + "events": [ + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "cluster_id", + "type": { + "displayName": ["ClusterId"], + "type": 18 + } + }, + { + "docs": [], + "indexed": true, + "label": "owner_id", + "type": { + "displayName": ["AccountId"], + "type": 21 + } + }, + { + "docs": [], + "indexed": false, + "label": "amount", + "type": { + "displayName": ["Balance"], + "type": 2 + } + } + ], + "docs": ["This event should be emitted for any customer who deposited funds."], + "label": "DdcBalanceDeposited", + "module_path": "ddc_primitives::contracts::customer_deposit::events", + "signature_topic": "0x5b6d40e103624b4c551be042e050915209a5ab49a928c3b181a06a3a1885e6f0" + }, + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "cluster_id", + "type": { + "displayName": ["ClusterId"], + "type": 18 + } + }, + { + "docs": [], + "indexed": true, + "label": "owner_id", + "type": { + "displayName": ["AccountId"], + "type": 21 + } + }, + { + "docs": [], + "indexed": false, + "label": "amount", + "type": { + "displayName": ["Balance"], + "type": 2 + } + } + ], + "docs": ["This event should be emitted for any customer who initiated unlocking of deposit."], + "label": "DdcBalanceUnlocked", + "module_path": "ddc_primitives::contracts::customer_deposit::events", + "signature_topic": "0x23675c3332cb2187e6029af300d9b5e14814a1212cfaec983f962988aa488653" + }, + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "cluster_id", + "type": { + "displayName": ["ClusterId"], + "type": 18 + } + }, + { + "docs": [], + "indexed": true, + "label": "owner_id", + "type": { + "displayName": ["AccountId"], + "type": 21 + } + }, + { + "docs": [], + "indexed": false, + "label": "amount", + "type": { + "displayName": ["Balance"], + "type": 2 + } + } + ], + "docs": ["This event should be emitted for any customer who did withdrawal of unlocked funds."], + "label": "DdcBalanceWithdrawn", + "module_path": "ddc_primitives::contracts::customer_deposit::events", + "signature_topic": "0x55b26822df868a289423cd7b3a691f8789e9bcb61d3e4cb994e6e8b593046dbd" + }, + { + "args": [ + { + "docs": [], + "indexed": true, + "label": "cluster_id", + "type": { + "displayName": ["ClusterId"], + "type": 18 + } + }, + { + "docs": [], + "indexed": true, + "label": "owner_id", + "type": { + "displayName": ["AccountId"], + "type": 21 + } + }, + { + "docs": [], + "indexed": false, + "label": "charged", + "type": { + "displayName": ["Balance"], + "type": 2 + } + }, + { + "docs": [], + "indexed": false, + "label": "expected", + "type": { + "displayName": ["Balance"], + "type": 2 + } + } + ], + "docs": ["This event should be emitted for any customer who has been charged during payouts."], + "label": "DdcBalanceCharged", + "module_path": "ddc_primitives::contracts::customer_deposit::events", + "signature_topic": "0x9db6acf54011fb413d2878834705faf7952aa27a12db083b35fca6c64ca2b2d0" + } + ], + "lang_error": { + "displayName": ["ink", "LangError"], + "type": 20 + }, + "messages": [ + { + "args": [ + { + "label": "owner", + "type": { + "displayName": ["AccountId32"], + "type": 21 + } + } + ], + "default": false, + "docs": [" Fetches customer balance in DDC cluster."], + "label": "DdcBalancesFetcher::get_balance", + "mutates": false, + "payable": false, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 22 + }, + "selector": "0xa40735c6" + }, + { + "args": [ + { + "label": "from_index", + "type": { + "displayName": ["u64"], + "type": 14 + } + }, + { + "label": "limit", + "type": { + "displayName": ["u64"], + "type": 14 + } + } + ], + "default": false, + "docs": [" Fetches customers balances in DDC cluster in a paginated manner."], + "label": "DdcBalancesFetcher::get_balances", + "mutates": false, + "payable": false, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 27 + }, + "selector": "0xbc7c9644" + }, + { + "args": [], + "default": false, + "docs": [" Top up deposit balance on behalf its owner."], + "label": "DdcBalancesDepositor::deposit", + "mutates": true, + "payable": true, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 29 + }, + "selector": "0x2d1d8745" + }, + { + "args": [ + { + "label": "owner", + "type": { + "displayName": ["AccountId32"], + "type": 21 + } + } + ], + "default": false, + "docs": [" Top up deposit balance for specific owner on behalf faucet."], + "label": "DdcBalancesDepositor::deposit_for", + "mutates": true, + "payable": true, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 29 + }, + "selector": "0xd8c1df42" + }, + { + "args": [ + { + "label": "value", + "type": { + "displayName": ["BalanceU128"], + "type": 2 + } + } + ], + "default": false, + "docs": [" Initiate unlocking of deposit balance on behalf its owner."], + "label": "DdcBalancesDepositor::unlock_deposit", + "mutates": true, + "payable": false, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 29 + }, + "selector": "0x91be5b57" + }, + { + "args": [], + "default": false, + "docs": [" Withdraw unlocked deposit balance on behalf its owner."], + "label": "DdcBalancesDepositor::withdraw_unlocked", + "mutates": true, + "payable": false, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 29 + }, + "selector": "0x52402e18" + }, + { + "args": [ + { + "label": "payout_vault", + "type": { + "displayName": ["AccountId32"], + "type": 21 + } + }, + { + "label": "batch", + "type": { + "displayName": ["Vec"], + "type": 33 + } + } + ], + "default": false, + "docs": [" Charges customers for DDC service usage while DAC-based payouts are in progress."], + "label": "DdcPayoutsPayer::charge", + "mutates": true, + "payable": false, + "returnType": { + "displayName": ["ink", "MessageResult"], + "type": 35 + }, + "selector": "0x18c2a328" + } + ] + }, + "storage": { + "root": { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "array": { + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 0 + } + }, + "len": 20, + "offset": "0x00000000" + } + }, + "name": "cluster_id" + }, + { + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 1 + } + }, + "name": "unlock_delay_blocks" + }, + { + "layout": { + "root": { + "layout": { + "struct": { + "fields": [ + { + "layout": { + "leaf": { + "key": "0x02c047f0", + "ty": 2 + } + }, + "name": "total" + }, + { + "layout": { + "leaf": { + "key": "0x02c047f0", + "ty": 2 + } + }, + "name": "active" + }, + { + "layout": { + "leaf": { + "key": "0x02c047f0", + "ty": 3 + } + }, + "name": "unlocking" + } + ], + "name": "CustomerLedger" + } + }, + "root_key": "0x02c047f0", + "ty": 5 + } + }, + "name": "balances" + }, + { + "layout": { + "root": { + "layout": { + "leaf": { + "key": "0x421d3cb3", + "ty": 6 + } + }, + "root_key": "0x421d3cb3", + "ty": 13 + } + }, + "name": "accounts" + }, + { + "layout": { + "leaf": { + "key": "0x00000000", + "ty": 14 + } + }, + "name": "count" + } + ], + "name": "CustomerDepositContract" + } + }, + "root_key": "0x00000000", + "ty": 17 + } + }, + "types": [ + { + "id": 0, + "type": { + "def": { + "primitive": "u8" + } + } + }, + { + "id": 1, + "type": { + "def": { + "primitive": "u32" + } + } + }, + { + "id": 2, + "type": { + "def": { + "primitive": "u128" + } + } + }, + { + "id": 3, + "type": { + "def": { + "sequence": { + "type": 4 + } + } + } + }, + { + "id": 4, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "value", + "type": 2, + "typeName": "Balance" + }, + { + "name": "block", + "type": 1, + "typeName": "BlockNumber" + } + ] + } + }, + "path": ["customer_deposit", "customer_deposit", "LinearUnlockChunk"] + } + }, + { + "id": 5, + "type": { + "def": { + "composite": {} + }, + "params": [ + { + "name": "K", + "type": 6 + }, + { + "name": "V", + "type": 8 + }, + { + "name": "KeyType", + "type": 9 + } + ], + "path": ["ink_storage", "lazy", "mapping", "Mapping"] + } + }, + { + "id": 6, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 7, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": ["ink_primitives", "types", "AccountId"] + } + }, + { + "id": 7, + "type": { + "def": { + "array": { + "len": 32, + "type": 0 + } + } + } + }, + { + "id": 8, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "total", + "type": 2, + "typeName": "Balance" + }, + { + "name": "active", + "type": 2, + "typeName": "Balance" + }, + { + "name": "unlocking", + "type": 3, + "typeName": "Vec" + } + ] + } + }, + "path": ["customer_deposit", "customer_deposit", "CustomerLedger"] + } + }, + { + "id": 9, + "type": { + "def": { + "composite": {} + }, + "params": [ + { + "name": "L", + "type": 10 + }, + { + "name": "R", + "type": 11 + } + ], + "path": ["ink_storage_traits", "impls", "ResolverKey"] + } + }, + { + "id": 10, + "type": { + "def": { + "composite": {} + }, + "path": ["ink_storage_traits", "impls", "AutoKey"] + } + }, + { + "id": 11, + "type": { + "def": { + "composite": {} + }, + "params": [ + { + "name": "ParentKey", + "type": 12 + } + ], + "path": ["ink_storage_traits", "impls", "ManualKey"] + } + }, + { + "id": 12, + "type": { + "def": { + "tuple": [] + } + } + }, + { + "id": 13, + "type": { + "def": { + "composite": {} + }, + "params": [ + { + "name": "K", + "type": 14 + }, + { + "name": "V", + "type": 6 + }, + { + "name": "KeyType", + "type": 15 + } + ], + "path": ["ink_storage", "lazy", "mapping", "Mapping"] + } + }, + { + "id": 14, + "type": { + "def": { + "primitive": "u64" + } + } + }, + { + "id": 15, + "type": { + "def": { + "composite": {} + }, + "params": [ + { + "name": "L", + "type": 10 + }, + { + "name": "R", + "type": 16 + } + ], + "path": ["ink_storage_traits", "impls", "ResolverKey"] + } + }, + { + "id": 16, + "type": { + "def": { + "composite": {} + }, + "params": [ + { + "name": "ParentKey", + "type": 12 + } + ], + "path": ["ink_storage_traits", "impls", "ManualKey"] + } + }, + { + "id": 17, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "cluster_id", + "type": 18, + "typeName": ",>>::Type" + }, + { + "name": "unlock_delay_blocks", + "type": 1, + "typeName": ",>>::Type" + }, + { + "name": "balances", + "type": 5, + "typeName": " as::ink::storage::traits::\nAutoStorableHint<::ink::storage::traits::ManualKey<4031234050u32,\n()>,>>::Type" + }, + { + "name": "accounts", + "type": 13, + "typeName": " as::ink::storage::traits::\nAutoStorableHint<::ink::storage::traits::ManualKey<3007061314u32,\n()>,>>::Type" + }, + { + "name": "count", + "type": 14, + "typeName": ",>>::Type" + } + ] + } + }, + "path": ["customer_deposit", "customer_deposit", "CustomerDepositContract"] + } + }, + { + "id": 18, + "type": { + "def": { + "array": { + "len": 20, + "type": 0 + } + } + } + }, + { + "id": 19, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 12 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 20 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 12 + }, + { + "name": "E", + "type": 20 + } + ], + "path": ["Result"] + } + }, + { + "id": 20, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 1, + "name": "CouldNotReadInput" + } + ] + } + }, + "path": ["ink_primitives", "LangError"] + } + }, + { + "id": 21, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 7, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": ["sp_core", "crypto", "AccountId32"] + } + }, + { + "id": 22, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 23 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 20 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 23 + }, + { + "name": "E", + "type": 20 + } + ], + "path": ["Result"] + } + }, + { + "id": 23, + "type": { + "def": { + "variant": { + "variants": [ + { + "index": 0, + "name": "None" + }, + { + "fields": [ + { + "type": 24 + } + ], + "index": 1, + "name": "Some" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 24 + } + ], + "path": ["Option"] + } + }, + { + "id": 24, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "owner", + "type": 21, + "typeName": "AccountId" + }, + { + "name": "total", + "type": 2, + "typeName": "Balance" + }, + { + "name": "active", + "type": 2, + "typeName": "Balance" + }, + { + "name": "unlocking", + "type": 25, + "typeName": "Vec" + } + ] + } + }, + "path": ["ddc_primitives", "contracts", "customer_deposit", "types", "Ledger"] + } + }, + { + "id": 25, + "type": { + "def": { + "sequence": { + "type": 26 + } + } + } + }, + { + "id": 26, + "type": { + "def": { + "composite": { + "fields": [ + { + "name": "value", + "type": 2, + "typeName": "Balance" + }, + { + "name": "block", + "type": 1, + "typeName": "BlockNumber" + } + ] + } + }, + "path": ["ddc_primitives", "contracts", "customer_deposit", "types", "UnlockChunk"] + } + }, + { + "id": 27, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 28 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 20 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 28 + }, + { + "name": "E", + "type": 20 + } + ], + "path": ["Result"] + } + }, + { + "id": 28, + "type": { + "def": { + "sequence": { + "type": 24 + } + } + } + }, + { + "id": 29, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 30 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 20 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 30 + }, + { + "name": "E", + "type": 20 + } + ], + "path": ["Result"] + } + }, + { + "id": 30, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 12 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 31 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 12 + }, + { + "name": "E", + "type": 31 + } + ], + "path": ["Result"] + } + }, + { + "id": 31, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 32, + "typeName": "u16" + } + ], + "index": 0, + "name": "Code" + } + ] + } + }, + "path": ["ddc_primitives", "contracts", "customer_deposit", "errors", "Error"] + } + }, + { + "id": 32, + "type": { + "def": { + "primitive": "u16" + } + } + }, + { + "id": 33, + "type": { + "def": { + "sequence": { + "type": 34 + } + } + } + }, + { + "id": 34, + "type": { + "def": { + "tuple": [21, 2] + } + } + }, + { + "id": 35, + "type": { + "def": { + "variant": { + "variants": [ + { + "fields": [ + { + "type": 33 + } + ], + "index": 0, + "name": "Ok" + }, + { + "fields": [ + { + "type": 20 + } + ], + "index": 1, + "name": "Err" + } + ] + } + }, + "params": [ + { + "name": "T", + "type": 33 + }, + { + "name": "E", + "type": 20 + } + ], + "path": ["Result"] + } + }, + { + "id": 36, + "type": { + "def": { + "composite": { + "fields": [ + { + "type": 7, + "typeName": "[u8; 32]" + } + ] + } + }, + "path": ["ink_primitives", "types", "Hash"] + } + }, + { + "id": 37, + "type": { + "def": { + "variant": {} + }, + "path": ["customer_deposit", "DdcPayoutsExtension"] + } + } + ], + "version": 5 +} diff --git a/packages/blockchain/src/index.ts b/packages/blockchain/src/index.ts index 4ca74e88..ce5a48cd 100644 --- a/packages/blockchain/src/index.ts +++ b/packages/blockchain/src/index.ts @@ -21,6 +21,7 @@ export * from './Signer'; * Pallets */ export * from './DDCCustomersPallet'; +export * from './CustomerDepositContract'; export * from './DDCStakingPallet'; export * from './DDCClustersPallet'; export * from './DDCNodesPallet'; diff --git a/packages/changelog-preset/package.json b/packages/changelog-preset/package.json index 4c4896f3..e4915b78 100644 --- a/packages/changelog-preset/package.json +++ b/packages/changelog-preset/package.json @@ -1,6 +1,6 @@ { "name": "@cere-ddc-sdk/conventional-changelog-changelog-preset", - "version": "2.15.0", + "version": "2.16.0", "private": true, "author": "Cere Network { - await client.depositBalance(options.clusterId, BigInt(amount * CERE), options); + await client.depositBalance(BigInt(amount * CERE)); const totalBalance = await client.getDeposit(options.clusterId); return Number(totalBalance / BigInt(CERE)); diff --git a/packages/ddc-client/package.json b/packages/ddc-client/package.json index 5a1b2f95..b0e5d5f9 100644 --- a/packages/ddc-client/package.json +++ b/packages/ddc-client/package.json @@ -1,7 +1,7 @@ { "name": "@cere-ddc-sdk/ddc-client", "description": "DDC client", - "version": "2.15.0", + "version": "2.16.0", "type": "module", "repository": { "type": "git", @@ -28,9 +28,9 @@ "docs": "typedoc" }, "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc": "2.15.0", - "@cere-ddc-sdk/file-storage": "2.15.0" + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc": "2.16.0", + "@cere-ddc-sdk/file-storage": "2.16.0" }, "publishConfig": { "access": "public" diff --git a/packages/ddc-client/src/DdcClient.ts b/packages/ddc-client/src/DdcClient.ts index e7e093db..c0a2bd14 100644 --- a/packages/ddc-client/src/DdcClient.ts +++ b/packages/ddc-client/src/DdcClient.ts @@ -19,19 +19,24 @@ import { CnsRecordGetOptions, } from '@cere-ddc-sdk/ddc'; import { FileStorage, File, FileStoreOptions, FileResponse, FileReadOptions } from '@cere-ddc-sdk/file-storage'; -import { AccountId, Blockchain, BucketId, BucketParams, ClusterId, Sendable } from '@cere-ddc-sdk/blockchain'; +import { + AccountId, + Blockchain, + BucketId, + BucketParams, + ClusterId, + CustomerDepositContract, + Ledger, +} from '@cere-ddc-sdk/blockchain'; import { DagNodeUri, DdcUri, FileUri } from './DdcUri'; export type DdcClientConfig = Omit & Omit & { blockchain: Blockchain | ConfigPreset['blockchain']; + customerDepositContractAddress: string; }; -type DepositBalanceOptions = { - allowExtra?: boolean; -}; - /** * `DdcClient` is a class that provides methods to interact with the DDC. * @@ -43,8 +48,9 @@ export class DdcClient { private readonly fileStorage: FileStorage; private readonly signer: Signer; private readonly logger: Logger; + private readonly customerDepositContract: CustomerDepositContract; - constructor(uriOrSigner: Signer | string, config: DdcClientConfig = DEFAULT_PRESET) { + constructor(uriOrSigner: Signer | string, config: DdcClientConfig) { const logger = createLogger('DdcClient', config); const blockchain = typeof config.blockchain === 'string' ? new Blockchain({ wsEndpoint: config.blockchain }) : config.blockchain; @@ -60,6 +66,8 @@ export class DdcClient { this.ddcNode = new BalancedNode({ ...config, router, logger }); this.fileStorage = new FileStorage(router, { ...config, logger }); + this.customerDepositContract = blockchain.getCustomerDepositContract(config.customerDepositContractAddress); + logger.debug(config, 'DdcClient created'); if (config.logErrors !== false) { @@ -67,6 +75,7 @@ export class DdcClient { 'getBalance', 'depositBalance', 'getDeposit', + 'getLedger', 'createBucket', 'getBucket', 'getBucketList', @@ -139,9 +148,7 @@ export class DdcClient { /** * Deposits a specified amount of tokens to the account for a specific cluster. The account must have enough tokens to cover the deposit. * - * @param clusterId - The ID of the cluster to deposit tokens for. * @param amount - The amount of tokens to deposit. - * @param options - Additional options for the deposit. * * @returns A promise that resolves to the transaction hash of the deposit. * @@ -155,22 +162,18 @@ export class DdcClient { * console.log(txHash); * ``` * */ - async depositBalance(clusterId: ClusterId, amount: bigint, options: DepositBalanceOptions = {}) { - let tx: Sendable; - const currentDeposit = - options.allowExtra === false - ? null - : await this.blockchain.ddcCustomers.getStackingInfo(clusterId, this.signer.address); - - if (currentDeposit === null) { - this.logger.info('Depositing balance %s to %s for cluster %s', amount, this.signer.address, clusterId); - tx = this.blockchain.ddcCustomers.deposit(clusterId, amount); - } else { - this.logger.info('Depositing extra balance %s to %s for cluster %s', amount, this.signer.address, clusterId); - tx = this.blockchain.ddcCustomers.depositExtra(clusterId, amount); - } + async depositBalance(amount: bigint) { + this.logger.info('Depositing balance %s to %s using smart contract', amount, this.signer.address); + const tx = this.customerDepositContract.deposit(amount); + const result = await this.blockchain.send(tx, { account: this.signer }); + + this.logger.info('Deposit transaction result:', result); + this.logger.info( + 'Deposit transaction events:', + result.events.map((e) => `${e.section}.${e.method}: ${JSON.stringify(e.data)}`), + ); - return this.blockchain.send(tx, { account: this.signer }); + return result; } /** @@ -178,7 +181,6 @@ export class DdcClient { * This allows depositing funds on behalf of another address. * * @param targetAddress - The target address to deposit funds for. - * @param clusterId - The ID of the cluster to deposit tokens for. * @param amount - The amount of tokens to deposit. * * @returns A promise that resolves to the transaction hash of the deposit. @@ -187,23 +189,21 @@ export class DdcClient { * * ```typescript * const targetAddress = '5D5PhZQNJzcJXVBxwJxZcsutjKPqUPydrvpu6HeiBfMae2Qu'; - * const clusterId: ClusterId = '0x...'; * const amount = 100n; - * const txHash = await ddcClient.depositBalanceFor(targetAddress, clusterId, amount); + * const txHash = await ddcClient.depositBalanceFor(targetAddress, amount); * * console.log(txHash); * ``` * */ - async depositBalanceFor(targetAddress: AccountId, clusterId: ClusterId, amount: bigint) { - this.logger.info('Depositing balance %s for %s in cluster %s', amount, targetAddress, clusterId); - const tx = this.blockchain.ddcCustomers.depositFor(targetAddress, clusterId, amount); + async depositBalanceFor(targetAddress: AccountId, amount: bigint) { + this.logger.info('Depositing balance %s for %s using smart contract', amount, targetAddress); + const tx = this.customerDepositContract.depositFor(targetAddress, amount); return this.blockchain.send(tx, { account: this.signer }); } /** * Retrieves the current active deposit of the account for a specific cluster. * - * @param clusterId - The ID of the cluster to get deposit for. * @param accountId - Optional account ID. If not provided, uses the signer's address. * * @returns A promise that resolves to the current active deposit of the account. @@ -211,26 +211,25 @@ export class DdcClient { * @example * * ```typescript - * const clusterId: ClusterId = '0x...'; * const deposit = await ddcClient.getDeposit(clusterId); * * console.log(deposit); * ``` * */ - async getDeposit(clusterId: ClusterId, accountId?: AccountId) { + async getDeposit(accountId?: AccountId) { const targetAccountId = accountId || this.signer.address; - this.logger.info('Getting the account deposit %s for cluster %s', targetAccountId, clusterId); - const info = await this.blockchain.ddcCustomers.getStackingInfo(clusterId, targetAccountId); - const deposit = BigInt(info?.active || 0n); - this.logger.info('The account (%s) deposit for cluster %s is %s', targetAccountId, clusterId, deposit); + this.logger.info('Getting the account deposit %s using smart contract', targetAccountId); + + const ledger = await this.customerDepositContract.getBalance(targetAccountId); + const deposit = ledger?.active || 0n; + this.logger.info('The account (%s) deposit is %s', targetAccountId, deposit); return deposit; } /** * Unlocks deposit funds from the account for the specified cluster. * - * @param clusterId - The ID of the cluster. * @param amount - The amount to unlock. * * @returns A promise that resolves to the transaction hash. @@ -238,41 +237,62 @@ export class DdcClient { * @example * * ```typescript - * const clusterId: ClusterId = '0x...'; * const amount = 100n; * const txHash = await ddcClient.unlockDeposit(clusterId, amount); * * console.log(txHash); * ``` * */ - async unlockDeposit(clusterId: ClusterId, amount: bigint) { - this.logger.info('Unlocking deposit %s for cluster %s', amount, clusterId); - const tx = this.blockchain.ddcCustomers.unlockDeposit(clusterId, amount); + async unlockDeposit(amount: bigint) { + this.logger.info('Unlocking deposit %s using smart contract', amount); + const tx = this.customerDepositContract.unlockDeposit(amount); return this.blockchain.send(tx, { account: this.signer }); } /** * Withdraws unlocked funds from the account for the specified cluster. * - * @param clusterId - The ID of the cluster. - * * @returns A promise that resolves to the transaction hash. * * @example * * ```typescript - * const clusterId: ClusterId = '0x...'; * const txHash = await ddcClient.withdrawUnlockedDeposit(clusterId); * * console.log(txHash); * ``` * */ - async withdrawUnlockedDeposit(clusterId: ClusterId) { - this.logger.info('Withdrawing unlocked deposit for cluster %s', clusterId); - const tx = this.blockchain.ddcCustomers.withdrawUnlockedDeposit(clusterId); + async withdrawUnlockedDeposit() { + this.logger.info('Withdrawing unlocked deposit using smart contract'); + const tx = this.customerDepositContract.withdrawUnlocked(); return this.blockchain.send(tx, { account: this.signer }); } + /** + * Gets full client (ledger) balance information from the smart contract. + * Includes active funds, total amount, and unlock information. + * + * @param accountId - Optional account ID. If not specified, the signer's address is used. + * @returns Promise that resolves to balance information, or null if not found + * + * @example + * ```typescript + * const ledger = await ddcClient.getLedger(); + * console.log('Active balance:', ledger?.active); + * console.log('Total balance:', ledger?.total); + * console.log('Unlocking chunks:', ledger?.unlocking); + * ``` + */ + async getLedger(accountId?: AccountId): Promise { + const targetAccountId = accountId || this.signer.address; + + this.logger.info('Getting ledger information for account %s', targetAccountId); + const ledger = await this.customerDepositContract.getBalance(targetAccountId); + this.logger.info('Got ledger: %o', ledger); + + return ledger; + } + /** * Creates a new bucket on a specified cluster. * diff --git a/packages/ddc/package.json b/packages/ddc/package.json index b811f7ca..71dc6b96 100644 --- a/packages/ddc/package.json +++ b/packages/ddc/package.json @@ -1,7 +1,7 @@ { "name": "@cere-ddc-sdk/ddc", "description": "DDC nodes API", - "version": "2.15.0", + "version": "2.16.0", "type": "module", "repository": { "type": "git", @@ -29,7 +29,7 @@ "docs": "typedoc" }, "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", "@grpc/grpc-js": "^1.9.13", "@protobuf-ts/grpc-transport": "^2.9.3", "@protobuf-ts/runtime": "^2.9.3", diff --git a/packages/ddc/src/presets.ts b/packages/ddc/src/presets.ts index 59905d1f..39d3ec82 100644 --- a/packages/ddc/src/presets.ts +++ b/packages/ddc/src/presets.ts @@ -4,6 +4,7 @@ import { RouterNode } from './routing'; export type ConfigPreset = { blockchain: string; nodes?: RouterNode[]; + customerDepositContractAddress: string; }; /** @@ -13,6 +14,7 @@ export type ConfigPreset = { */ export const MAINNET: ConfigPreset = { blockchain: 'wss://rpc.mainnet.cere.network/ws', + customerDepositContractAddress: '', // @TODO }; /** @@ -22,6 +24,7 @@ export const MAINNET: ConfigPreset = { */ export const TESTNET: ConfigPreset = { blockchain: 'wss://rpc.testnet.cere.network/ws', + customerDepositContractAddress: '', // @TODO }; /** @@ -31,6 +34,7 @@ export const TESTNET: ConfigPreset = { */ export const DEVNET: ConfigPreset = { blockchain: 'wss://archive.devnet.cere.network/ws', + customerDepositContractAddress: '6TZJb1s7PMa9UcHnjickVtiNG2JjYN6wNYU3CTMvji1VxTMY', }; export const DEFAULT_PRESET = TESTNET; @@ -45,6 +49,7 @@ export const DEFAULT_PRESET = TESTNET; */ export const DEVNET_STATIC: ConfigPreset = { blockchain: 'wss://archive.devnet.cere.network/ws', + customerDepositContractAddress: '6TZJb1s7PMa9UcHnjickVtiNG2JjYN6wNYU3CTMvji1VxTMY', nodes: [ { mode: StorageNodeMode.Full, @@ -110,6 +115,7 @@ export const DEVNET_STATIC: ConfigPreset = { */ export const TESTNET_STATIC: ConfigPreset = { blockchain: 'wss://rpc.testnet.cere.network/ws', + customerDepositContractAddress: '', // @TODO nodes: [ { mode: StorageNodeMode.Full, diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index bfc81888..211e5e14 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@cere-ddc-sdk/eslint-config", - "version": "2.15.0", + "version": "2.16.0", "main": "index.js", "private": true, "author": "Cere Network ({ })); const bcPresets = { - devnet: { ...DEVNET, baseUrl: 'https://storage.devnet.cere.network' }, - testnet: { ...TESTNET, baseUrl: 'https://storage.testnet.cere.network' }, - mainnet: { ...MAINNET, baseUrl: 'https://storage.dragon.cere.network' }, + devnet: { + ...DEVNET, + baseUrl: 'https://storage.devnet.cere.network', + customerDepositContractAddress: DEVNET.customerDepositContractAddress, + }, + testnet: { + ...TESTNET, + baseUrl: 'https://storage.testnet.cere.network', + customerDepositContractAddress: TESTNET.customerDepositContractAddress, + }, + mainnet: { + ...MAINNET, + baseUrl: 'https://storage.dragon.cere.network', + customerDepositContractAddress: MAINNET.customerDepositContractAddress, + }, custom: { blockchain: __BC_ENDPOINT__ || '', baseUrl: 'http://localhost:8091', + customerDepositContractAddress: DEVNET.customerDepositContractAddress, }, }; @@ -245,7 +258,12 @@ export const Playground = () => { try { setInProgress(true); const blockchain = await Blockchain.connect({ wsEndpoint: preset.blockchain }); - const client = await DdcClient.create(signer!, { ...preset, blockchain, logLevel: 'debug' }); + const client = await DdcClient.create(signer!, { + ...preset, + blockchain, + logLevel: 'debug', + customerDepositContractAddress: bcPresets[selectedBc]?.customerDepositContractAddress ?? '', + }); const [clusters, balance] = await Promise.all([blockchain.ddcClusters.listClusters(), client.getBalance()]); setBlockchain(blockchain); @@ -280,7 +298,7 @@ export const Playground = () => { try { setInProgress(true); - await client!.depositBalance(currentClusterId as ClusterId, BigInt(extraDeposit) * CERE); + await client!.depositBalance(BigInt(extraDeposit) * CERE); const updatedDeposit = await client!.getDeposit(currentClusterId as ClusterId); setDeposit(blockchain!.formatBalance(updatedDeposit, false)); setStep(step + 1); diff --git a/tests/helpers/constants.ts b/tests/helpers/constants.ts index 5b01d3e7..84f27df9 100644 --- a/tests/helpers/constants.ts +++ b/tests/helpers/constants.ts @@ -5,6 +5,6 @@ export const DDC_BLOCK_SIZE = 16 * 1024; export const ROOT_USER_SEED = 'hybrid label reunion only dawn maze asset draft cousin height flock nation'; export const ROOT_ACCOUNT_TYPE = 'sr25519'; export const BLOCKCHAIN_MAX_BLOCK_WEIGHT = 2_000_000_000_000; -export const STORAGE_NODE_MAX_STARTUP_TIME = 60_000; +export const STORAGE_NODE_MAX_STARTUP_TIME = 120_000; export const BLOCKCHAIN_NODE_MAX_STARTUP_TIME = 30_000; export const DDC_CLUSTER_STAKE = 100_000; // CERE tokens diff --git a/tests/helpers/ddc.ts b/tests/helpers/ddc.ts index 319adfa7..4d829530 100644 --- a/tests/helpers/ddc.ts +++ b/tests/helpers/ddc.ts @@ -53,5 +53,6 @@ type ClientOptions = Pick; export const getClientConfig = (options: ClientOptions = {}): DdcClientConfig => ({ blockchain: BLOCKCHAIN_RPC_URL, logLevel: options.logLevel || 'silent', + customerDepositContractAddress: '6TZJb1s7PMa9UcHnjickVtiNG2JjYN6wNYU3CTMvji1VxTMY', ...options, }); diff --git a/tests/package.json b/tests/package.json index d9e6d13a..710dd896 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,6 +1,6 @@ { "name": "@cere-ddc-sdk/tests", - "version": "2.15.0", + "version": "2.16.0", "description": "Cere DDC SDK tests", "private": true, "scripts": { @@ -9,10 +9,10 @@ "test:env": "ts-node ./scripts/env.ts" }, "dependencies": { - "@cere-ddc-sdk/blockchain": "2.15.0", - "@cere-ddc-sdk/ddc": "2.15.0", - "@cere-ddc-sdk/ddc-client": "2.15.0", - "@cere-ddc-sdk/file-storage": "2.15.0", + "@cere-ddc-sdk/blockchain": "2.16.0", + "@cere-ddc-sdk/ddc": "2.16.0", + "@cere-ddc-sdk/ddc-client": "2.16.0", + "@cere-ddc-sdk/file-storage": "2.16.0", "@types/jest": "^29.5.11", "internal-ip": "6.2.0", "jest": "^29.7.0", diff --git a/tests/setup/environment/blockchain.ts b/tests/setup/environment/blockchain.ts index 6b8d445f..d5c72bbe 100644 --- a/tests/setup/environment/blockchain.ts +++ b/tests/setup/environment/blockchain.ts @@ -16,6 +16,7 @@ import { getHostIP, getStorageNodes, BLOCKCHAIN_NODE_MAX_STARTUP_TIME, + ROOT_USER_SEED, } from '../../helpers'; export type BlockchainConfig = BlockchainState & { @@ -79,7 +80,7 @@ export const stopBlockchain = async () => { }; export const setupBlockchain = async () => { - console.group('Setup pallets'); + console.group('Setup blockchain with smart contracts'); console.time('Done'); await cryptoWaitReady(); @@ -163,17 +164,38 @@ export const setupBlockchain = async () => { ); console.timeEnd('Add nodes to cluster'); - console.time('Create buckets'); - const bucketsSendResult = await blockchain.batchAllSend( - [ - blockchain.ddcCustomers.deposit(clusterId, 100n * CERE), - blockchain.ddcCustomers.createBucket(clusterId, { isPublic: true }), // 1n - public bucket - blockchain.ddcCustomers.createBucket(clusterId, { isPublic: false }), // 2n - private bucket - ], - { account: rootAccount }, - ); - const createdBucketIds = blockchain.ddcCustomers.extractCreatedBucketIds(bucketsSendResult.events); - console.timeEnd('Create buckets'); + // Setup customer with smart contract + console.time('Setup customer with smart contract'); + const CUSTOMER_DEPOSIT_CONTRACT_ADDRESS = '6TZJb1s7PMa9UcHnjickVtiNG2JjYN6wNYU3CTMvji1VxTMY'; + const customerDepositContract = blockchain.getCustomerDepositContract(CUSTOMER_DEPOSIT_CONTRACT_ADDRESS); + + // Make a deposit for the customer (100 CERE) + const depositTx = customerDepositContract.deposit(100n * CERE); + await blockchain.send(depositTx, { account: rootAccount }); + console.timeEnd('Setup customer with smart contract'); + + // Create buckets through DDC Client (which uses smart contracts) + console.time('Create buckets through DDC Client'); + const { DdcClient } = await import('@cere-ddc-sdk/ddc-client'); + const { UriSigner } = await import('@cere-ddc-sdk/blockchain'); + + const signer = new UriSigner(ROOT_USER_SEED); + await signer.isReady(); + + const ddcClient = await DdcClient.create(signer, { + blockchain: 'ws://localhost:9944', + customerDepositContractAddress: CUSTOMER_DEPOSIT_CONTRACT_ADDRESS, + logLevel: 'debug', + nodes: [], + }); + + // Create buckets through client + const publicBucket = await ddcClient.createBucket(clusterId, { isPublic: true }); + const privateBucket = await ddcClient.createBucket(clusterId, { isPublic: false }); + const createdBucketIds = [publicBucket, privateBucket]; + + await ddcClient.disconnect(); + console.timeEnd('Create buckets through DDC Client'); console.timeEnd('Done'); console.groupEnd(); diff --git a/tests/specs/DdcClient.spec.ts b/tests/specs/DdcClient.spec.ts index 28d3f153..cedb38cc 100644 --- a/tests/specs/DdcClient.spec.ts +++ b/tests/specs/DdcClient.spec.ts @@ -205,7 +205,7 @@ describe('DDC Client', () => { test('Deposit balance', async () => { const toDeposit = 10n * CERE; const prevDeposit = await client.getDeposit(clusterId); - await client.depositBalance(clusterId, toDeposit); + await client.depositBalance(toDeposit); const nextDeposit = await client.getDeposit(clusterId); expect(nextDeposit - prevDeposit).toEqual(toDeposit);