Skip to content

Commit 300d3fe

Browse files
committed
fix specs
1 parent 1a6dde0 commit 300d3fe

File tree

7 files changed

+97
-32
lines changed

7 files changed

+97
-32
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { GenericContractApi } from 'dedot/contracts';
2+
import { ISubstrateClient } from 'dedot';
3+
import { BalanceInsufficientError } from '../utils/index.js';
4+
5+
export const checkBalanceSufficiency = async <T extends GenericContractApi = GenericContractApi>(
6+
client: ISubstrateClient<T['types']['ChainApi']>,
7+
address: string,
8+
): Promise<void> => {
9+
const balance = await client.query.system.account(address);
10+
// TODO better calculate reducible balance
11+
if (balance.data.free <= 0n) {
12+
throw new BalanceInsufficientError(address);
13+
}
14+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './balance.js'

packages/typink/src/hooks/__tests__/useContractTx.test.ts

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@ import { renderHook, act } from '@testing-library/react';
33
import { useContractTx } from '../useContractTx.js';
44
import { useTypink } from '../useTypink.js';
55
import { Contract } from 'dedot/contracts';
6-
import { waitForNextUpdate } from './test-utils.js';
6+
import { sleep, waitForNextUpdate } from './test-utils.js';
7+
import { checkBalanceSufficiency } from '../../helpers/index.js';
8+
import { BalanceInsufficientError } from '../../utils/index.js';
9+
import { useDeployerTx } from '../useDeployerTx.js';
710

811
// Mock the useTypink hook
912
vi.mock('../useTypink', () => ({
1013
useTypink: vi.fn(),
1114
}));
1215

16+
vi.mock('../../helpers', () => ({
17+
checkBalanceSufficiency: vi.fn(),
18+
}));
19+
1320
describe('useContractTx', () => {
1421
const randomGasLimit = { refTime: 1000n, proofSize: 1000n };
1522
let mockContract: Contract<any>;
1623
let mockConnectedAccount: { address: string };
1724

1825
beforeEach(() => {
1926
mockContract = {
27+
client: vi.fn(),
2028
tx: {
2129
message: vi.fn(),
2230
},
@@ -30,6 +38,8 @@ describe('useContractTx', () => {
3038
(useTypink as any).mockReturnValue({
3139
connectedAccount: mockConnectedAccount,
3240
});
41+
42+
(checkBalanceSufficiency as any).mockImplementation(() => Promise.resolve(true));
3343
});
3444

3545
it('should return the correct structure', () => {
@@ -56,7 +66,7 @@ describe('useContractTx', () => {
5666
),
5767
);
5868

59-
await expect(result.current.signAndSend({} as any)).rejects.toThrow('Contract Not Found');
69+
await expect(result.current.signAndSend({} as any)).rejects.toThrow('Contract not found');
6070
});
6171

6272
it('should throw an error if connectedAccount is undefined', async () => {
@@ -72,7 +82,9 @@ describe('useContractTx', () => {
7282
),
7383
);
7484

75-
await expect(result.current.signAndSend({} as any)).rejects.toThrow('Connected Account Not Found');
85+
await expect(result.current.signAndSend({} as any)).rejects.toThrow(
86+
'No connected account. Please connect your wallet.',
87+
);
7688
});
7789

7890
it('should call the contract method with correct parameters', async () => {
@@ -165,6 +177,26 @@ describe('useContractTx', () => {
165177
),
166178
);
167179

168-
await expect(result.current.signAndSend({ args: [] })).rejects.toThrow('Contract error');
180+
await expect(result.current.signAndSend({ args: [] })).rejects.toThrow('Contract Message Error: Contract error');
181+
});
182+
183+
it('should throw an error when balance is insufficient', async () => {
184+
vi.mocked(checkBalanceSufficiency).mockRejectedValue(new BalanceInsufficientError(mockConnectedAccount.address));
185+
186+
const { result } = renderHook(() =>
187+
useContractTx(
188+
mockContract,
189+
// @ts-ignore
190+
'message',
191+
),
192+
);
193+
194+
await expect(result.current.signAndSend({ args: [] })).rejects.toThrow(
195+
new BalanceInsufficientError(mockConnectedAccount.address),
196+
);
197+
198+
expect(checkBalanceSufficiency).toHaveBeenCalledWith(expect.anything(), 'mock-address');
199+
expect(mockContract.query.message).not.toHaveBeenCalled();
200+
expect(mockContract.tx.message).not.toHaveBeenCalled();
169201
});
170202
});

packages/typink/src/hooks/__tests__/useDeployerTx.test.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
1-
import { describe, it, expect, vi, beforeEach } from 'vitest';
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
22
import { renderHook } from '@testing-library/react';
33
import { useTypink } from '../useTypink.js';
44
import { ContractDeployer } from 'dedot/contracts';
55
import { waitForNextUpdate } from './test-utils.js';
66
import { useDeployerTx } from '../useDeployerTx.js';
7+
import { checkBalanceSufficiency } from '../../helpers/index.js';
8+
import { BalanceInsufficientError } from '../../utils/index.js';
79

810
// Mock the useTypink hook
911
vi.mock('../useTypink', () => ({
1012
useTypink: vi.fn(),
1113
}));
1214

15+
vi.mock('../../helpers', () => ({
16+
checkBalanceSufficiency: vi.fn(),
17+
}));
18+
1319
describe('useDeployerTx', () => {
1420
const randomGasLimit = { refTime: 1000n, proofSize: 1000n };
1521
let mockContractDeployer: ContractDeployer<any>;
1622
let mockConnectedAccount: { address: string };
1723

1824
beforeEach(() => {
1925
mockContractDeployer = {
26+
client: vi.fn(),
2027
tx: {
2128
message: vi.fn(),
2229
},
@@ -30,6 +37,8 @@ describe('useDeployerTx', () => {
3037
(useTypink as any).mockReturnValue({
3138
connectedAccount: mockConnectedAccount,
3239
});
40+
41+
(checkBalanceSufficiency as any).mockImplementation(() => Promise.resolve(true));
3342
});
3443

3544
it('should return the correct structure', () => {
@@ -56,7 +65,7 @@ describe('useDeployerTx', () => {
5665
),
5766
);
5867

59-
await expect(result.current.signAndSend({} as any)).rejects.toThrow('Contract Deployer Not Found');
68+
await expect(result.current.signAndSend({} as any)).rejects.toThrow('ContractDeployer not found');
6069
});
6170

6271
it('should throw an error if connectedAccount is undefined', async () => {
@@ -72,7 +81,9 @@ describe('useDeployerTx', () => {
7281
),
7382
);
7483

75-
await expect(result.current.signAndSend({} as any)).rejects.toThrow('Connected Account Not Found');
84+
await expect(result.current.signAndSend({} as any)).rejects.toThrow(
85+
'No connected account. Please connect your wallet.',
86+
);
7687
});
7788

7889
it('should call the contract method with correct parameters', async () => {
@@ -171,6 +182,26 @@ describe('useDeployerTx', () => {
171182
),
172183
);
173184

174-
await expect(result.current.signAndSend({ args: [] })).rejects.toThrow('Contract error');
185+
await expect(result.current.signAndSend({ args: [] })).rejects.toThrow('Contract Message Error: Contract error');
186+
});
187+
188+
it('should throw an error when balance is insufficient', async () => {
189+
vi.mocked(checkBalanceSufficiency).mockRejectedValue(new BalanceInsufficientError(mockConnectedAccount.address));
190+
191+
const { result } = renderHook(() =>
192+
useDeployerTx(
193+
mockContractDeployer,
194+
// @ts-ignore
195+
'message',
196+
),
197+
);
198+
199+
await expect(result.current.signAndSend({ args: [] })).rejects.toThrow(
200+
new BalanceInsufficientError(mockConnectedAccount.address),
201+
);
202+
203+
expect(checkBalanceSufficiency).toHaveBeenCalledWith(expect.anything(), 'mock-address');
204+
expect(mockContractDeployer.query.message).not.toHaveBeenCalled();
205+
expect(mockContractDeployer.tx.message).not.toHaveBeenCalled();
175206
});
176207
});

packages/typink/src/hooks/useContractTx.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import {
1010
} from 'dedot/contracts';
1111
import { ISubmittableResult } from 'dedot/types';
1212
import { assert, deferred } from 'dedot/utils';
13-
import { BalanceInsufficientError, ContractMessageError } from '../utils/index.js';
13+
import { ContractMessageError } from '../utils/index.js';
1414
import { useDeepDeps } from './internal/index.js';
15-
import { ISubstrateClient } from 'dedot';
15+
import { checkBalanceSufficiency } from '../helpers/index.js';
1616

1717
type UseContractTx<A extends GenericContractApi = GenericContractApi> = OmitNever<{
1818
[K in keyof A['tx']]: K extends string ? (K extends `${infer Literal}` ? Literal : never) : never;
@@ -128,7 +128,7 @@ export async function contractTx<
128128
const signAndSend = async () => {
129129
const { contract, fn, args = [], caller, txOptions = {}, callback } = parameters;
130130

131-
await checkBalanceSufficient(contract.client, caller);
131+
await checkBalanceSufficiency(contract.client, caller);
132132

133133
try {
134134
const dryRunOptions: ContractCallOptions = { caller };
@@ -176,14 +176,3 @@ export async function contractTx<
176176

177177
return defer.promise;
178178
}
179-
180-
const checkBalanceSufficient = async <T extends GenericContractApi = GenericContractApi>(
181-
client: ISubstrateClient<T['types']['ChainApi']>,
182-
caller: string,
183-
): Promise<void> => {
184-
const balance = await client.query.system.account(caller);
185-
// TODO better calculate reducible balance
186-
if (balance.data.free <= 0n) {
187-
throw new BalanceInsufficientError(caller);
188-
}
189-
};

packages/typink/src/hooks/useDeployerTx.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import {
1111
} from 'dedot/contracts';
1212
import { IEventRecord, IRuntimeEvent, ISubmittableResult } from 'dedot/types';
1313
import { assert, deferred } from 'dedot/utils';
14-
import { TypinkError } from '../utils/index.js';
14+
import { ContractMessageError } from '../utils/index.js';
1515
import { Hash } from 'dedot/codecs';
1616
import { useDeepDeps } from './internal/index.js';
17+
import { checkBalanceSufficiency } from '../helpers/index.js';
1718

1819
type UseDeployerTx<A extends GenericContractApi = GenericContractApi> = OmitNever<{
1920
[K in keyof A['constructorTx']]: K extends string ? (K extends `${infer Literal}` ? Literal : never) : never;
@@ -70,8 +71,8 @@ export function useDeployerTx<
7071
const signAndSend = useMemo(
7172
() => {
7273
return async (o: Parameters<UseDeployerTxReturnType<T>['signAndSend']>[0]) => {
73-
assert(deployer, 'Contract Deployer Not Found');
74-
assert(connectedAccount, 'Connected Account Not Found');
74+
assert(deployer, 'ContractDeployer not found');
75+
assert(connectedAccount, 'No connected account. Please connect your wallet.');
7576

7677
setInProgress(true);
7778
setInBestBlockProgress(true);
@@ -139,16 +140,14 @@ export async function deployerTx<
139140
callback?: (result: ISubmittableResult) => void;
140141
} & Args<Pop<Parameters<T['tx'][M]>>>,
141142
): Promise<void> {
142-
// TODO assertions
143-
// TODO check if balance is sufficient
144-
145143
const defer = deferred<void>();
146144

147145
const signAndSend = async () => {
148146
const { deployer, fn, args = [], caller, txOptions = {}, callback } = parameters;
149147

150148
try {
151-
// TODO dry running
149+
await checkBalanceSufficiency(deployer.client, caller);
150+
152151
const dryRunOptions: ConstructorCallOptions = { caller };
153152

154153
const dryRun = await deployer.query[fn](...args, dryRunOptions);
@@ -160,8 +159,7 @@ export async function deployerTx<
160159
} = dryRun;
161160

162161
if (data && data['isErr'] && data['err']) {
163-
// TODO Add a specific contract level error
164-
throw new TypinkError(JSON.stringify(data['err']));
162+
throw new ContractMessageError(data['err']);
165163
}
166164

167165
const actualTxOptions: ContractTxOptions = {

packages/typink/src/networks/mainnet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { JsonRpcApi, NetworkInfo } from '../types';
1+
import { JsonRpcApi, NetworkInfo } from '../types.js';
22

33
export const alephZero: NetworkInfo = {
44
id: 'alephzero',

0 commit comments

Comments
 (0)