Skip to content

Commit d7b0b75

Browse files
Merge pull request #93 from jcam1/fix-decimal-handling-in-jpyc-token-transfers
Fix Decimals Handling
2 parents db771cf + e77d669 commit d7b0b75

File tree

5 files changed

+118
-44
lines changed

5 files changed

+118
-44
lines changed

packages/core/src/interfaces/jpyc.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,22 @@ export interface IJPYC {
2222
* @param minter - Minter address
2323
* @returns minter allowance
2424
*/
25-
minterAllowance(params: { minter: Address }): Promise<Uint256>;
25+
minterAllowance(params: { minter: Address }): Promise<number>;
2626

2727
/**
2828
* Returns total supply of JPYC tokens.
2929
*
3030
* @returns total supply
3131
*/
32-
totalSupply(): Promise<Uint256>;
32+
totalSupply(): Promise<number>;
3333

3434
/**
3535
* Returns balance of an account.
3636
*
3737
* @param account - Account address
3838
* @returns account balance
3939
*/
40-
balanceOf(params: { account: Address }): Promise<Uint256>;
40+
balanceOf(params: { account: Address }): Promise<number>;
4141

4242
/**
4343
* Returns allowance of a spender over owner's tokens (for transferring).
@@ -46,7 +46,7 @@ export interface IJPYC {
4646
* @param spender - Spender address
4747
* @returns spender allowance
4848
*/
49-
allowance(params: { owner: Address; spender: Address }): Promise<Uint256>;
49+
allowance(params: { owner: Address; spender: Address }): Promise<number>;
5050

5151
/**
5252
* Returns nonce for EIP2612's `permit`.
@@ -67,7 +67,7 @@ export interface IJPYC {
6767
* @param minterAllowedAmount - Minter allowance
6868
* @returns transaction hash
6969
*/
70-
configureMinter(params: { minter: Address; minterAllowedAmount: Uint256 }): Promise<Hash>;
70+
configureMinter(params: { minter: Address; minterAllowedAmount: number }): Promise<Hash>;
7171

7272
/**
7373
* Mints tokens.
@@ -76,7 +76,7 @@ export interface IJPYC {
7676
* @param amount - Amount of tokens to mint
7777
* @returns transaction hash
7878
*/
79-
mint(params: { to: Address; amount: Uint256 }): Promise<Hash>;
79+
mint(params: { to: Address; amount: number }): Promise<Hash>;
8080

8181
/**
8282
* Transfers tokens (directly).
@@ -85,7 +85,7 @@ export interface IJPYC {
8585
* @param value - Amount of tokens to transfer
8686
* @returns transaction hash
8787
*/
88-
transfer(params: { to: Address; value: Uint256 }): Promise<Hash>;
88+
transfer(params: { to: Address; value: number }): Promise<Hash>;
8989

9090
/**
9191
* Transfers tokens (from spender).
@@ -95,7 +95,7 @@ export interface IJPYC {
9595
* @param value - Amount of tokens to transfer
9696
* @returns transaction hash
9797
*/
98-
transferFrom(params: { from: Address; to: Address; value: Uint256 }): Promise<Hash>;
98+
transferFrom(params: { from: Address; to: Address; value: number }): Promise<Hash>;
9999

100100
/**
101101
* Transfers tokens with authorization.
@@ -114,7 +114,7 @@ export interface IJPYC {
114114
transferWithAuthorization(params: {
115115
from: Address;
116116
to: Address;
117-
value: Uint256;
117+
value: number;
118118
validAfter: Uint256;
119119
validBefore: Uint256;
120120
nonce: Bytes32;
@@ -140,7 +140,7 @@ export interface IJPYC {
140140
receiveWithAuthorization(params: {
141141
from: Address;
142142
to: Address;
143-
value: Uint256;
143+
value: number;
144144
validAfter: Uint256;
145145
validBefore: Uint256;
146146
nonce: Bytes32;
@@ -174,7 +174,7 @@ export interface IJPYC {
174174
* @param value - Amount of allowance
175175
* @returns transaction hash
176176
*/
177-
approve(params: { spender: Address; value: Uint256 }): Promise<Hash>;
177+
approve(params: { spender: Address; value: number }): Promise<Hash>;
178178

179179
/**
180180
* Increases allowance.
@@ -183,7 +183,7 @@ export interface IJPYC {
183183
* @param increment - Amount of allowance to increase
184184
* @returns transaction hash
185185
*/
186-
increaseAllowance(params: { spender: Address; increment: Uint256 }): Promise<Hash>;
186+
increaseAllowance(params: { spender: Address; increment: number }): Promise<Hash>;
187187

188188
/**
189189
* Decreases allowance.
@@ -192,7 +192,7 @@ export interface IJPYC {
192192
* @param increment - Amount of allowance to decrease
193193
* @returns transaction hash
194194
*/
195-
decreaseAllowance(params: { spender: Address; decrement: Uint256 }): Promise<Hash>;
195+
decreaseAllowance(params: { spender: Address; decrement: number }): Promise<Hash>;
196196

197197
/**
198198
* Sets allowance of a spender over owner's tokens, given owner's signed approval.
@@ -209,7 +209,7 @@ export interface IJPYC {
209209
permit(params: {
210210
owner: Address;
211211
spender: Address;
212-
value: Uint256;
212+
value: number;
213213
deadline: Uint256;
214214
v: Uint8;
215215
r: Bytes32;

packages/core/src/jpyc.ts

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import { getContract, GetContractReturnType, Hash, WalletClient } from 'viem';
44
import { IJPYC, JPYC_V2_ABI } from './interfaces';
55
import {
66
Address,
7+
restoreDecimals,
78
Bytes32,
89
InvalidAddressError,
910
InvalidTransactionError,
1011
isValidAddress,
1112
LOCAL_PROXY_ADDRESS,
13+
removeDecimals,
14+
toUint256,
1215
V2_PROXY_ADDRESS,
1316
} from './utils';
1417

@@ -39,42 +42,42 @@ export class JPYC implements IJPYC {
3942
return resp as boolean;
4043
}
4144

42-
async minterAllowance(params: { minter: Address }): Promise<Uint256> {
45+
async minterAllowance(params: { minter: Address }): Promise<number> {
4346
const resp = await this.contract.read.minterAllowance([params.minter]);
4447

45-
return Uint256.from((resp as bigint).toString());
48+
return restoreDecimals(Uint256.from(resp as string));
4649
}
4750

48-
async totalSupply(): Promise<Uint256> {
51+
async totalSupply(): Promise<number> {
4952
const resp = await this.contract.read.totalSupply();
5053

51-
return Uint256.from((resp as bigint).toString());
54+
return restoreDecimals(Uint256.from(resp as string));
5255
}
5356

54-
async balanceOf(params: { account: Address }): Promise<Uint256> {
57+
async balanceOf(params: { account: Address }): Promise<number> {
5558
const resp = await this.contract.read.balanceOf([params.account]);
5659

57-
return Uint256.from((resp as bigint).toString());
60+
return restoreDecimals(Uint256.from(resp as string));
5861
}
5962

60-
async allowance(params: { owner: Address; spender: Address }): Promise<Uint256> {
63+
async allowance(params: { owner: Address; spender: Address }): Promise<number> {
6164
const resp = await this.contract.read.allowance([params.owner, params.spender]);
6265

63-
return Uint256.from((resp as bigint).toString());
66+
return restoreDecimals(Uint256.from(resp as string));
6467
}
6568

6669
async nonces(params: { owner: Address }): Promise<Uint256> {
6770
const resp = await this.contract.read.nonces([params.owner]);
6871

69-
return Uint256.from((resp as bigint).toString());
72+
return toUint256(resp as bigint);
7073
}
7174

7275
/**
73-
* Regular Functions
76+
* Mutation Functions
7477
*/
7578

76-
async configureMinter(params: { minter: Address; minterAllowedAmount: Uint256 }): Promise<Hash> {
77-
const args = [params.minter, params.minterAllowedAmount];
79+
async configureMinter(params: { minter: Address; minterAllowedAmount: number }): Promise<Hash> {
80+
const args = [params.minter, removeDecimals(params.minterAllowedAmount)];
7881

7982
try {
8083
await this.contract.simulate.configureMinter(args);
@@ -85,8 +88,8 @@ export class JPYC implements IJPYC {
8588
return await this.contract.write.configureMinter(args);
8689
}
8790

88-
async mint(params: { to: Address; amount: Uint256 }): Promise<Hash> {
89-
const args = [params.to, params.amount];
91+
async mint(params: { to: Address; amount: number }): Promise<Hash> {
92+
const args = [params.to, removeDecimals(params.amount)];
9093

9194
try {
9295
await this.contract.simulate.mint(args);
@@ -97,8 +100,8 @@ export class JPYC implements IJPYC {
97100
return await this.contract.write.mint(args);
98101
}
99102

100-
async transfer(params: { to: Address; value: Uint256 }): Promise<Hash> {
101-
const args = [params.to, params.value];
103+
async transfer(params: { to: Address; value: number }): Promise<Hash> {
104+
const args = [params.to, removeDecimals(params.value)];
102105

103106
try {
104107
await this.contract.simulate.transfer(args);
@@ -109,8 +112,8 @@ export class JPYC implements IJPYC {
109112
return await this.contract.write.transfer(args);
110113
}
111114

112-
async transferFrom(params: { from: Address; to: Address; value: Uint256 }): Promise<Hash> {
113-
const args = [params.from, params.to, params.value];
115+
async transferFrom(params: { from: Address; to: Address; value: number }): Promise<Hash> {
116+
const args = [params.from, params.to, removeDecimals(params.value)];
114117

115118
try {
116119
await this.contract.simulate.transferFrom(args);
@@ -124,7 +127,7 @@ export class JPYC implements IJPYC {
124127
async transferWithAuthorization(params: {
125128
from: Address;
126129
to: Address;
127-
value: Uint256;
130+
value: number;
128131
validAfter: Uint256;
129132
validBefore: Uint256;
130133
nonce: Bytes32;
@@ -135,7 +138,7 @@ export class JPYC implements IJPYC {
135138
const args = [
136139
params.from,
137140
params.to,
138-
params.value,
141+
removeDecimals(params.value),
139142
params.validAfter,
140143
params.validBefore,
141144
params.nonce,
@@ -156,7 +159,7 @@ export class JPYC implements IJPYC {
156159
async receiveWithAuthorization(params: {
157160
from: Address;
158161
to: Address;
159-
value: Uint256;
162+
value: number;
160163
validAfter: Uint256;
161164
validBefore: Uint256;
162165
nonce: Bytes32;
@@ -167,7 +170,7 @@ export class JPYC implements IJPYC {
167170
const args = [
168171
params.from,
169172
params.to,
170-
params.value,
173+
removeDecimals(params.value),
171174
params.validAfter,
172175
params.validBefore,
173176
params.nonce,
@@ -203,8 +206,8 @@ export class JPYC implements IJPYC {
203206
return await this.contract.write.cancelAuthorization(args);
204207
}
205208

206-
async approve(params: { spender: Address; value: Uint256 }): Promise<Hash> {
207-
const args = [params.spender, params.value];
209+
async approve(params: { spender: Address; value: number }): Promise<Hash> {
210+
const args = [params.spender, removeDecimals(params.value)];
208211

209212
try {
210213
await this.contract.simulate.approve(args);
@@ -215,8 +218,8 @@ export class JPYC implements IJPYC {
215218
return await this.contract.write.approve(args);
216219
}
217220

218-
async increaseAllowance(params: { spender: Address; increment: Uint256 }): Promise<Hash> {
219-
const args = [params.spender, params.increment];
221+
async increaseAllowance(params: { spender: Address; increment: number }): Promise<Hash> {
222+
const args = [params.spender, removeDecimals(params.increment)];
220223

221224
try {
222225
await this.contract.simulate.increaseAllowance(args);
@@ -227,8 +230,8 @@ export class JPYC implements IJPYC {
227230
return await this.contract.write.increaseAllowance(args);
228231
}
229232

230-
async decreaseAllowance(params: { spender: Address; decrement: Uint256 }): Promise<Hash> {
231-
const args = [params.spender, params.decrement];
233+
async decreaseAllowance(params: { spender: Address; decrement: number }): Promise<Hash> {
234+
const args = [params.spender, removeDecimals(params.decrement)];
232235

233236
try {
234237
await this.contract.simulate.decreaseAllowance(args);
@@ -242,7 +245,7 @@ export class JPYC implements IJPYC {
242245
async permit(params: {
243246
owner: Address;
244247
spender: Address;
245-
value: Uint256;
248+
value: number;
246249
deadline: Uint256;
247250
v: Uint8;
248251
r: Bytes32;
@@ -251,7 +254,7 @@ export class JPYC implements IJPYC {
251254
const args = [
252255
params.owner,
253256
params.spender,
254-
params.value,
257+
removeDecimals(params.value),
255258
params.deadline,
256259
params.v,
257260
params.r,

packages/core/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './addresses';
22
export * from './chains';
33
export * from './errors';
4+
export * from './math';
45
export * from './types';

packages/core/src/utils/math.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Uint256 } from 'soltypes';
2+
3+
import { toUint256, removeDecimals, restoreDecimals } from './math';
4+
5+
describe('Unit tests of toUint256()', () => {
6+
test('converts bigint to uint256 value', () => {
7+
const res = toUint256(123456789n);
8+
expect(res).toBeInstanceOf(Uint256);
9+
expect(res.toString()).toStrictEqual('123456789');
10+
});
11+
});
12+
13+
describe('Unit tests of removeDecimals()', () => {
14+
test('removes decimal places from a decimal', () => {
15+
const res = removeDecimals(0.01);
16+
expect(res).toBeInstanceOf(Uint256);
17+
expect(res.toString()).toStrictEqual('10000000000000000');
18+
});
19+
20+
test('removes decimal places from an integer (no decimal places)', () => {
21+
const res = removeDecimals(100);
22+
expect(res).toBeInstanceOf(Uint256);
23+
expect(res.toString()).toStrictEqual('100000000000000000000');
24+
});
25+
});
26+
27+
describe('Unit tests of restoreDecimals()', () => {
28+
test('restores decimal places to bigint', () => {
29+
const res = restoreDecimals(Uint256.from('100'));
30+
expect(typeof res).toBe('number');
31+
expect(res).toStrictEqual(0.0000000000000001);
32+
});
33+
});

packages/core/src/utils/math.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Uint256 } from 'soltypes';
2+
3+
const TOKEN_DECIMALS = 18;
4+
const DECIMALS_QUANTIFIER = 10 ** TOKEN_DECIMALS;
5+
6+
/**
7+
* Converts bigint to uint256
8+
* @param value bigint value
9+
* @returns value as uint256
10+
*/
11+
export const toUint256 = (value: bigint): Uint256 => {
12+
return Uint256.from(value.toString());
13+
};
14+
15+
/**
16+
* Removes decimal places to make sure that only integer values live on-chain.
17+
* e.g.)
18+
* 0.01 -> 10,000,000,000,000,000
19+
* 100 -> 100,000,000,000,000,000,000
20+
* @param value integer or decimal value
21+
* @returns value as uint256
22+
*/
23+
export const removeDecimals = (value: number): Uint256 => {
24+
return toUint256(BigInt(value * DECIMALS_QUANTIFIER));
25+
};
26+
27+
/**
28+
* Restores decimal places (mainly for display purpose).
29+
* e.g.)
30+
* 10,000,000,000,000,000 -> 0.01
31+
* 100,000,000,000,000,000,000 -> 100
32+
* @param value uint256 value
33+
* @returns value as number (i.e., integer or decimal)
34+
*/
35+
export const restoreDecimals = (value: Uint256): number => {
36+
return Number(value.toString()) / DECIMALS_QUANTIFIER;
37+
};

0 commit comments

Comments
 (0)