@chipi-stack/core 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +91 -0
- package/dist/estimation/estimate-fee.d.ts +21 -0
- package/dist/estimation/estimate-fee.d.ts.map +1 -0
- package/dist/estimation/estimate-fee.js +34 -0
- package/dist/estimation/estimate-fee.js.map +1 -0
- package/dist/estimation/types.d.ts +14 -0
- package/dist/estimation/types.d.ts.map +1 -0
- package/dist/estimation/types.js +2 -0
- package/dist/estimation/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/signers/direct-signer.d.ts +18 -0
- package/dist/signers/direct-signer.d.ts.map +1 -0
- package/dist/signers/direct-signer.js +32 -0
- package/dist/signers/direct-signer.js.map +1 -0
- package/dist/signers/external-signer.d.ts +17 -0
- package/dist/signers/external-signer.d.ts.map +1 -0
- package/dist/signers/external-signer.js +19 -0
- package/dist/signers/external-signer.js.map +1 -0
- package/dist/signers/passkey-signer.d.ts +25 -0
- package/dist/signers/passkey-signer.d.ts.map +1 -0
- package/dist/signers/passkey-signer.js +24 -0
- package/dist/signers/passkey-signer.js.map +1 -0
- package/dist/signers/types.d.ts +19 -0
- package/dist/signers/types.d.ts.map +1 -0
- package/dist/signers/types.js +2 -0
- package/dist/signers/types.js.map +1 -0
- package/dist/tokens/amount.d.ts +72 -0
- package/dist/tokens/amount.d.ts.map +1 -0
- package/dist/tokens/amount.js +166 -0
- package/dist/tokens/amount.js.map +1 -0
- package/dist/tokens/erc20.d.ts +38 -0
- package/dist/tokens/erc20.d.ts.map +1 -0
- package/dist/tokens/erc20.js +79 -0
- package/dist/tokens/erc20.js.map +1 -0
- package/dist/tokens/presets/mainnet.json +38 -0
- package/dist/tokens/presets/sepolia.json +20 -0
- package/dist/tokens/registry.d.ts +27 -0
- package/dist/tokens/registry.d.ts.map +1 -0
- package/dist/tokens/registry.js +38 -0
- package/dist/tokens/registry.js.map +1 -0
- package/dist/transactions/tx-builder.d.ts +56 -0
- package/dist/transactions/tx-builder.d.ts.map +1 -0
- package/dist/transactions/tx-builder.js +104 -0
- package/dist/transactions/tx-builder.js.map +1 -0
- package/dist/transactions/types.d.ts +11 -0
- package/dist/transactions/types.d.ts.map +1 -0
- package/dist/transactions/types.js +2 -0
- package/dist/transactions/types.js.map +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Chipi Pay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @chipi-stack/core
|
|
2
|
+
|
|
3
|
+
Core primitives for Chipi Pay SDK: transaction building, token math, signer adapters, and fee estimation.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @chipi-stack/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Modules
|
|
12
|
+
|
|
13
|
+
### TxBuilder
|
|
14
|
+
|
|
15
|
+
Build Starknet multicall transactions with a fluent API:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { TxBuilder } from "@chipi-stack/core";
|
|
19
|
+
|
|
20
|
+
const tx = TxBuilder.new()
|
|
21
|
+
.transfer({ token: "USDC", to: "0x...", amount: "10.00" })
|
|
22
|
+
.approve({ token: "USDC", spender: "0x...", amount: "50.00" })
|
|
23
|
+
.build();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### TokenRegistry
|
|
27
|
+
|
|
28
|
+
Look up token metadata (address, decimals) by symbol and network:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { TokenRegistry } from "@chipi-stack/core";
|
|
32
|
+
|
|
33
|
+
const usdc = TokenRegistry.get("USDC", "mainnet");
|
|
34
|
+
// { address: "0x033068...", decimals: 6, symbol: "USDC" }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Amount
|
|
38
|
+
|
|
39
|
+
Safe token amount math with decimal precision:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { Amount } from "@chipi-stack/core";
|
|
43
|
+
|
|
44
|
+
const amt = Amount.fromHuman("10.50", 6); // 10.5 USDC
|
|
45
|
+
amt.toRaw(); // 10500000n
|
|
46
|
+
amt.toHuman(); // "10.50"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Signers
|
|
50
|
+
|
|
51
|
+
Unified `SignerAdapter` interface with three implementations:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { DirectSigner, ExternalSigner, PasskeySigner } from "@chipi-stack/core";
|
|
55
|
+
|
|
56
|
+
// Local key (testing only)
|
|
57
|
+
const direct = new DirectSigner("0x...");
|
|
58
|
+
|
|
59
|
+
// External provider (Privy, Cartridge, etc.)
|
|
60
|
+
const external = new ExternalSigner({
|
|
61
|
+
getPublicKey: () => provider.getPublicKey(),
|
|
62
|
+
sign: (hash) => provider.sign(hash),
|
|
63
|
+
getAccountAddress: () => provider.getAddress(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Passkey-based signer
|
|
67
|
+
const passkey = new PasskeySigner({
|
|
68
|
+
authenticateFn: (challenge) => passkeyAuth(challenge),
|
|
69
|
+
publicKey: "0x...",
|
|
70
|
+
accountAddress: "0x...",
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Fee Estimation
|
|
75
|
+
|
|
76
|
+
Estimate Starknet transaction fees before execution:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { estimateFee, preflight } from "@chipi-stack/core";
|
|
80
|
+
|
|
81
|
+
const fee = await estimateFee(calls, account);
|
|
82
|
+
const check = await preflight(calls, account); // fee + balance check
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
Full docs at [docs.chipipay.com](https://docs.chipipay.com)
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Account, Call } from "starknet";
|
|
2
|
+
import type { FeeEstimate, PreflightResult } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Estimate invocation fees for a set of calls.
|
|
5
|
+
* Does NOT execute the transaction.
|
|
6
|
+
*
|
|
7
|
+
* @param calls - Array of Starknet calls to estimate
|
|
8
|
+
* @param account - Starknet Account instance
|
|
9
|
+
* @throws If calls array is empty
|
|
10
|
+
*/
|
|
11
|
+
export declare function estimateFee(calls: Call[], account: Account): Promise<FeeEstimate>;
|
|
12
|
+
/**
|
|
13
|
+
* Simulate a transaction without submitting it.
|
|
14
|
+
* Returns trace and fee estimation.
|
|
15
|
+
*
|
|
16
|
+
* @param calls - Array of Starknet calls to simulate
|
|
17
|
+
* @param account - Starknet Account instance
|
|
18
|
+
* @throws If calls array is empty
|
|
19
|
+
*/
|
|
20
|
+
export declare function preflight(calls: Call[], account: Account): Promise<PreflightResult>;
|
|
21
|
+
//# sourceMappingURL=estimate-fee.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimate-fee.d.ts","sourceRoot":"","sources":["../../src/estimation/estimate-fee.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAIvF;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CASzF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Estimate invocation fees for a set of calls.
|
|
3
|
+
* Does NOT execute the transaction.
|
|
4
|
+
*
|
|
5
|
+
* @param calls - Array of Starknet calls to estimate
|
|
6
|
+
* @param account - Starknet Account instance
|
|
7
|
+
* @throws If calls array is empty
|
|
8
|
+
*/
|
|
9
|
+
export async function estimateFee(calls, account) {
|
|
10
|
+
if (calls.length === 0)
|
|
11
|
+
throw new Error("No calls to estimate");
|
|
12
|
+
const result = await account.estimateInvokeFee(calls);
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Simulate a transaction without submitting it.
|
|
17
|
+
* Returns trace and fee estimation.
|
|
18
|
+
*
|
|
19
|
+
* @param calls - Array of Starknet calls to simulate
|
|
20
|
+
* @param account - Starknet Account instance
|
|
21
|
+
* @throws If calls array is empty
|
|
22
|
+
*/
|
|
23
|
+
export async function preflight(calls, account) {
|
|
24
|
+
if (calls.length === 0)
|
|
25
|
+
throw new Error("No calls to preflight");
|
|
26
|
+
const result = await account.simulateTransaction([{ type: "INVOKE", payload: calls }], {
|
|
27
|
+
skipValidate: false,
|
|
28
|
+
});
|
|
29
|
+
if (!result || result.length === 0) {
|
|
30
|
+
throw new Error("Simulation returned no results");
|
|
31
|
+
}
|
|
32
|
+
return result[0];
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=estimate-fee.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimate-fee.js","sourceRoot":"","sources":["../../src/estimation/estimate-fee.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAa,EAAE,OAAgB;IAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,MAAgC,CAAC;AAC1C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAa,EAAE,OAAgB;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE;QAC9F,YAAY,EAAE,KAAK;KACpB,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,CAA+B,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface FeeEstimate {
|
|
2
|
+
overall_fee: bigint;
|
|
3
|
+
gas_consumed: bigint;
|
|
4
|
+
gas_price: bigint;
|
|
5
|
+
data_gas_consumed?: bigint;
|
|
6
|
+
data_gas_price?: bigint;
|
|
7
|
+
}
|
|
8
|
+
export interface PreflightResult {
|
|
9
|
+
transaction_trace: unknown;
|
|
10
|
+
fee_estimation: {
|
|
11
|
+
overall_fee: bigint;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/estimation/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/estimation/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Amount, type TokenInfo } from "./tokens/amount.js";
|
|
2
|
+
export { Erc20 } from "./tokens/erc20.js";
|
|
3
|
+
export { TokenRegistry, type Network } from "./tokens/registry.js";
|
|
4
|
+
export { TxBuilder } from "./transactions/tx-builder.js";
|
|
5
|
+
export type { TransferTarget, PreflightResult } from "./transactions/types.js";
|
|
6
|
+
export { DirectSigner } from "./signers/direct-signer.js";
|
|
7
|
+
export { PasskeySigner } from "./signers/passkey-signer.js";
|
|
8
|
+
export { ExternalSigner } from "./signers/external-signer.js";
|
|
9
|
+
export type { SignerAdapter, SignResult } from "./signers/types.js";
|
|
10
|
+
export { estimateFee, preflight } from "./estimation/estimate-fee.js";
|
|
11
|
+
export type { FeeEstimate } from "./estimation/types.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,KAAK,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGnE,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAG/E,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGpE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACtE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Tokens
|
|
2
|
+
export { Amount } from "./tokens/amount.js";
|
|
3
|
+
export { Erc20 } from "./tokens/erc20.js";
|
|
4
|
+
export { TokenRegistry } from "./tokens/registry.js";
|
|
5
|
+
// Transactions
|
|
6
|
+
export { TxBuilder } from "./transactions/tx-builder.js";
|
|
7
|
+
// Signers
|
|
8
|
+
export { DirectSigner } from "./signers/direct-signer.js";
|
|
9
|
+
export { PasskeySigner } from "./signers/passkey-signer.js";
|
|
10
|
+
export { ExternalSigner } from "./signers/external-signer.js";
|
|
11
|
+
// Estimation
|
|
12
|
+
export { estimateFee, preflight } from "./estimation/estimate-fee.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,SAAS;AACT,OAAO,EAAE,MAAM,EAAkB,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAgB,MAAM,sBAAsB,CAAC;AAEnE,eAAe;AACf,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,aAAa;AACb,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SignerAdapter, SignResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Signer backed by a local private key.
|
|
4
|
+
* Equivalent to StarkZap's StarkSigner.
|
|
5
|
+
*
|
|
6
|
+
* WARNING: Only for development/testing.
|
|
7
|
+
* Never hardcode private keys in production.
|
|
8
|
+
*/
|
|
9
|
+
export declare class DirectSigner implements SignerAdapter {
|
|
10
|
+
private readonly privateKey;
|
|
11
|
+
private readonly publicKey;
|
|
12
|
+
private readonly accountAddress;
|
|
13
|
+
constructor(privateKey: string, accountAddress?: string);
|
|
14
|
+
getPublicKey(): Promise<string>;
|
|
15
|
+
sign(messageHash: string): Promise<SignResult>;
|
|
16
|
+
getAccountAddress(): Promise<string>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=direct-signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct-signer.d.ts","sourceRoot":"","sources":["../../src/signers/direct-signer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;GAMG;AACH,qBAAa,YAAa,YAAW,aAAa;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,UAAU,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;IAWjD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAI/B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAQ9C,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;CAG3C"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ec, encode } from "starknet";
|
|
2
|
+
/**
|
|
3
|
+
* Signer backed by a local private key.
|
|
4
|
+
* Equivalent to StarkZap's StarkSigner.
|
|
5
|
+
*
|
|
6
|
+
* WARNING: Only for development/testing.
|
|
7
|
+
* Never hardcode private keys in production.
|
|
8
|
+
*/
|
|
9
|
+
export class DirectSigner {
|
|
10
|
+
constructor(privateKey, accountAddress) {
|
|
11
|
+
if (!privateKey || !/^0x[0-9a-fA-F]+$/.test(privateKey)) {
|
|
12
|
+
throw new Error("Invalid private key: must be a hex string starting with 0x");
|
|
13
|
+
}
|
|
14
|
+
this.privateKey = privateKey;
|
|
15
|
+
this.publicKey = encode.addHexPrefix(encode.buf2hex(ec.starkCurve.getPublicKey(privateKey, true)));
|
|
16
|
+
this.accountAddress = accountAddress ?? this.publicKey;
|
|
17
|
+
}
|
|
18
|
+
async getPublicKey() {
|
|
19
|
+
return this.publicKey;
|
|
20
|
+
}
|
|
21
|
+
async sign(messageHash) {
|
|
22
|
+
const signature = ec.starkCurve.sign(messageHash, this.privateKey);
|
|
23
|
+
return {
|
|
24
|
+
r: encode.addHexPrefix(signature.r.toString(16)),
|
|
25
|
+
s: encode.addHexPrefix(signature.s.toString(16)),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async getAccountAddress() {
|
|
29
|
+
return this.accountAddress;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=direct-signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct-signer.js","sourceRoot":"","sources":["../../src/signers/direct-signer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGtC;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IAKvB,YAAY,UAAkB,EAAE,cAAuB;QACrD,IAAI,CAAC,UAAU,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAChF,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,YAAY,CAClC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAC7D,CAAC;QACF,IAAI,CAAC,cAAc,GAAG,cAAc,IAAI,IAAI,CAAC,SAAS,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAmB;QAC5B,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACnE,OAAO;YACL,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAChD,CAAC,EAAE,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;SACjD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SignerAdapter, SignResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Adapter for external signer providers (Privy, Cartridge, etc.).
|
|
4
|
+
* Consumers provide the signing function — ExternalSigner just wraps the interface.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ExternalSigner implements SignerAdapter {
|
|
7
|
+
private readonly opts;
|
|
8
|
+
constructor(opts: {
|
|
9
|
+
getPublicKey: () => Promise<string>;
|
|
10
|
+
sign: (messageHash: string) => Promise<SignResult>;
|
|
11
|
+
getAccountAddress: () => Promise<string>;
|
|
12
|
+
});
|
|
13
|
+
getPublicKey(): Promise<string>;
|
|
14
|
+
sign(messageHash: string): Promise<SignResult>;
|
|
15
|
+
getAccountAddress(): Promise<string>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=external-signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-signer.d.ts","sourceRoot":"","sources":["../../src/signers/external-signer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D;;;GAGG;AACH,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAInB;gBAEU,IAAI,EAAE;QAChB,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;QACnD,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KAC1C;IAID,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAG/B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAG9C,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;CAGrC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter for external signer providers (Privy, Cartridge, etc.).
|
|
3
|
+
* Consumers provide the signing function — ExternalSigner just wraps the interface.
|
|
4
|
+
*/
|
|
5
|
+
export class ExternalSigner {
|
|
6
|
+
constructor(opts) {
|
|
7
|
+
this.opts = opts;
|
|
8
|
+
}
|
|
9
|
+
getPublicKey() {
|
|
10
|
+
return this.opts.getPublicKey();
|
|
11
|
+
}
|
|
12
|
+
sign(messageHash) {
|
|
13
|
+
return this.opts.sign(messageHash);
|
|
14
|
+
}
|
|
15
|
+
getAccountAddress() {
|
|
16
|
+
return this.opts.getAccountAddress();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=external-signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-signer.js","sourceRoot":"","sources":["../../src/signers/external-signer.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,cAAc;IAOzB,YAAY,IAIX;QACC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,WAAmB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IACD,iBAAiB;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACvC,CAAC;CACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { SignerAdapter, SignResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Signer that wraps @chipi-stack/passkey functions.
|
|
4
|
+
* Does NOT modify @chipi-stack/passkey — only wraps it.
|
|
5
|
+
*
|
|
6
|
+
* @param authenticateFn - The authenticatePasskey function from @chipi-stack/passkey
|
|
7
|
+
* @param publicKey - The passkey public key (obtained during registration)
|
|
8
|
+
*/
|
|
9
|
+
export declare class PasskeySigner implements SignerAdapter {
|
|
10
|
+
private readonly authenticateFn;
|
|
11
|
+
private readonly pubKey;
|
|
12
|
+
private readonly accountAddress;
|
|
13
|
+
constructor(opts: {
|
|
14
|
+
authenticateFn: (challenge: string) => Promise<{
|
|
15
|
+
r: string;
|
|
16
|
+
s: string;
|
|
17
|
+
}>;
|
|
18
|
+
publicKey: string;
|
|
19
|
+
accountAddress: string;
|
|
20
|
+
});
|
|
21
|
+
getPublicKey(): Promise<string>;
|
|
22
|
+
sign(messageHash: string): Promise<SignResult>;
|
|
23
|
+
getAccountAddress(): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=passkey-signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passkey-signer.d.ts","sourceRoot":"","sources":["../../src/signers/passkey-signer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;GAMG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2D;IAC1F,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,IAAI,EAAE;QAChB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACzE,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;KACxB;IAMK,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAI/B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;CAG3C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signer that wraps @chipi-stack/passkey functions.
|
|
3
|
+
* Does NOT modify @chipi-stack/passkey — only wraps it.
|
|
4
|
+
*
|
|
5
|
+
* @param authenticateFn - The authenticatePasskey function from @chipi-stack/passkey
|
|
6
|
+
* @param publicKey - The passkey public key (obtained during registration)
|
|
7
|
+
*/
|
|
8
|
+
export class PasskeySigner {
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
this.authenticateFn = opts.authenticateFn;
|
|
11
|
+
this.pubKey = opts.publicKey;
|
|
12
|
+
this.accountAddress = opts.accountAddress;
|
|
13
|
+
}
|
|
14
|
+
async getPublicKey() {
|
|
15
|
+
return this.pubKey;
|
|
16
|
+
}
|
|
17
|
+
async sign(messageHash) {
|
|
18
|
+
return this.authenticateFn(messageHash);
|
|
19
|
+
}
|
|
20
|
+
async getAccountAddress() {
|
|
21
|
+
return this.accountAddress;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=passkey-signer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passkey-signer.js","sourceRoot":"","sources":["../../src/signers/passkey-signer.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAKxB,YAAY,IAIX;QACC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAmB;QAC5B,OAAO,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Result of a signing operation. */
|
|
2
|
+
export interface SignResult {
|
|
3
|
+
r: string;
|
|
4
|
+
s: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Unified signer interface.
|
|
8
|
+
* Implementations wrap different key management strategies
|
|
9
|
+
* (local key, passkey, external provider).
|
|
10
|
+
*/
|
|
11
|
+
export interface SignerAdapter {
|
|
12
|
+
/** Get the public key associated with this signer. */
|
|
13
|
+
getPublicKey(): Promise<string>;
|
|
14
|
+
/** Sign a message hash. */
|
|
15
|
+
sign(messageHash: string): Promise<SignResult>;
|
|
16
|
+
/** Get the account address derived from this signer. */
|
|
17
|
+
getAccountAddress(): Promise<string>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/signers/types.ts"],"names":[],"mappings":"AAAA,qCAAqC;AACrC,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhC,2BAA2B;IAC3B,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAE/C,wDAAwD;IACxD,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/signers/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Minimal token info needed by Amount. */
|
|
2
|
+
export interface TokenInfo {
|
|
3
|
+
symbol: string;
|
|
4
|
+
decimals: number;
|
|
5
|
+
address: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Immutable representation of a token amount.
|
|
9
|
+
* Stores raw base units (e.g., wei for ETH, micro-units for USDC).
|
|
10
|
+
* All arithmetic is exact BigInt math — no floating point.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Amount {
|
|
13
|
+
/** Raw value in base units (smallest denomination). */
|
|
14
|
+
readonly raw: bigint;
|
|
15
|
+
/** Number of decimal places for this token. */
|
|
16
|
+
readonly decimals: number;
|
|
17
|
+
/** Token symbol (e.g., "USDC", "ETH"). */
|
|
18
|
+
readonly symbol: string;
|
|
19
|
+
private constructor();
|
|
20
|
+
private static assertValidDecimals;
|
|
21
|
+
/**
|
|
22
|
+
* Parse a human-readable string into an Amount.
|
|
23
|
+
* @param value - Human-readable value (e.g., "1.5")
|
|
24
|
+
* @param token - Token info with decimals and symbol
|
|
25
|
+
* @throws If value is negative, non-numeric, or has too many decimal places
|
|
26
|
+
*/
|
|
27
|
+
static parse(value: string, token: TokenInfo): Amount;
|
|
28
|
+
/**
|
|
29
|
+
* Create an Amount from a raw bigint value (base units).
|
|
30
|
+
* @param raw - Raw value in base units
|
|
31
|
+
* @param decimals - Token decimal places
|
|
32
|
+
* @param symbol - Token symbol (defaults to "")
|
|
33
|
+
*/
|
|
34
|
+
static fromRaw(raw: bigint, decimals: number, symbol?: string): Amount;
|
|
35
|
+
/** Add two amounts. Both must have the same decimals. */
|
|
36
|
+
add(other: Amount): Amount;
|
|
37
|
+
/**
|
|
38
|
+
* Subtract an amount.
|
|
39
|
+
* @throws If result would be negative
|
|
40
|
+
*/
|
|
41
|
+
subtract(other: Amount): Amount;
|
|
42
|
+
/**
|
|
43
|
+
* Multiply by a scalar bigint.
|
|
44
|
+
* @throws If scalar is negative
|
|
45
|
+
*/
|
|
46
|
+
multiply(scalar: bigint): Amount;
|
|
47
|
+
/**
|
|
48
|
+
* Divide by a scalar bigint. Truncates (no rounding).
|
|
49
|
+
* @throws If divisor is zero or negative
|
|
50
|
+
*/
|
|
51
|
+
divide(divisor: bigint): Amount;
|
|
52
|
+
eq(other: Amount): boolean;
|
|
53
|
+
gt(other: Amount): boolean;
|
|
54
|
+
gte(other: Amount): boolean;
|
|
55
|
+
lt(other: Amount): boolean;
|
|
56
|
+
lte(other: Amount): boolean;
|
|
57
|
+
isZero(): boolean;
|
|
58
|
+
/** Returns the raw bigint value (base units). */
|
|
59
|
+
toBase(): bigint;
|
|
60
|
+
/**
|
|
61
|
+
* Returns human-readable string (e.g., "1.5").
|
|
62
|
+
* Trailing zeros after decimal are preserved to one place minimum.
|
|
63
|
+
*/
|
|
64
|
+
toUnit(): string;
|
|
65
|
+
/**
|
|
66
|
+
* Returns formatted string with symbol (e.g., "1.5 USDC").
|
|
67
|
+
* @param compressed - If true, shorten large numbers (e.g., "1.5M USDC")
|
|
68
|
+
*/
|
|
69
|
+
toFormatted(compressed?: boolean): string;
|
|
70
|
+
private assertSameDecimals;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=amount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.d.ts","sourceRoot":"","sources":["../../src/tokens/amount.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,qBAAa,MAAM;IACjB,uDAAuD;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,OAAO;IAMP,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAQlC;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM;IAkBrD;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM;IAQlE,yDAAyD;IACzD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAK1B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQ/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAKhC;;;OAGG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAQ/B,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI1B,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI1B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3B,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI1B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3B,MAAM,IAAI,OAAO;IAMjB,iDAAiD;IACjD,MAAM,IAAI,MAAM;IAIhB;;;OAGG;IACH,MAAM,IAAI,MAAM;IAYhB;;;OAGG;IACH,WAAW,CAAC,UAAU,UAAQ,GAAG,MAAM;IAwBvC,OAAO,CAAC,kBAAkB;CAY3B"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Immutable representation of a token amount.
|
|
3
|
+
* Stores raw base units (e.g., wei for ETH, micro-units for USDC).
|
|
4
|
+
* All arithmetic is exact BigInt math — no floating point.
|
|
5
|
+
*/
|
|
6
|
+
export class Amount {
|
|
7
|
+
constructor(raw, decimals, symbol) {
|
|
8
|
+
this.raw = raw;
|
|
9
|
+
this.decimals = decimals;
|
|
10
|
+
this.symbol = symbol;
|
|
11
|
+
}
|
|
12
|
+
static assertValidDecimals(decimals) {
|
|
13
|
+
if (!Number.isInteger(decimals) || decimals < 0) {
|
|
14
|
+
throw new Error("Token decimals must be a non-negative integer");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// ── Factory Methods ──────────────────────────────────────────
|
|
18
|
+
/**
|
|
19
|
+
* Parse a human-readable string into an Amount.
|
|
20
|
+
* @param value - Human-readable value (e.g., "1.5")
|
|
21
|
+
* @param token - Token info with decimals and symbol
|
|
22
|
+
* @throws If value is negative, non-numeric, or has too many decimal places
|
|
23
|
+
*/
|
|
24
|
+
static parse(value, token) {
|
|
25
|
+
Amount.assertValidDecimals(token.decimals);
|
|
26
|
+
if (!/^\d+(\.\d+)?$/.test(value)) {
|
|
27
|
+
throw new Error(`Invalid amount: "${value}" — must be a non-negative number`);
|
|
28
|
+
}
|
|
29
|
+
const [whole, fraction = ""] = value.split(".");
|
|
30
|
+
if (fraction.length > token.decimals) {
|
|
31
|
+
throw new Error(`Too many decimal places: ${fraction.length} > ${token.decimals} for ${token.symbol}`);
|
|
32
|
+
}
|
|
33
|
+
const padded = fraction.padEnd(token.decimals, "0");
|
|
34
|
+
const raw = BigInt(whole + padded);
|
|
35
|
+
return new Amount(raw, token.decimals, token.symbol);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create an Amount from a raw bigint value (base units).
|
|
39
|
+
* @param raw - Raw value in base units
|
|
40
|
+
* @param decimals - Token decimal places
|
|
41
|
+
* @param symbol - Token symbol (defaults to "")
|
|
42
|
+
*/
|
|
43
|
+
static fromRaw(raw, decimals, symbol = "") {
|
|
44
|
+
if (raw < 0n)
|
|
45
|
+
throw new Error("Amount cannot be negative");
|
|
46
|
+
Amount.assertValidDecimals(decimals);
|
|
47
|
+
return new Amount(raw, decimals, symbol);
|
|
48
|
+
}
|
|
49
|
+
// ── Arithmetic ───────────────────────────────────────────────
|
|
50
|
+
/** Add two amounts. Both must have the same decimals. */
|
|
51
|
+
add(other) {
|
|
52
|
+
this.assertSameDecimals(other);
|
|
53
|
+
return new Amount(this.raw + other.raw, this.decimals, this.symbol);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Subtract an amount.
|
|
57
|
+
* @throws If result would be negative
|
|
58
|
+
*/
|
|
59
|
+
subtract(other) {
|
|
60
|
+
this.assertSameDecimals(other);
|
|
61
|
+
if (this.raw < other.raw) {
|
|
62
|
+
throw new Error("Subtraction would result in negative amount");
|
|
63
|
+
}
|
|
64
|
+
return new Amount(this.raw - other.raw, this.decimals, this.symbol);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Multiply by a scalar bigint.
|
|
68
|
+
* @throws If scalar is negative
|
|
69
|
+
*/
|
|
70
|
+
multiply(scalar) {
|
|
71
|
+
if (scalar < 0n)
|
|
72
|
+
throw new Error("Scalar cannot be negative");
|
|
73
|
+
return new Amount(this.raw * scalar, this.decimals, this.symbol);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Divide by a scalar bigint. Truncates (no rounding).
|
|
77
|
+
* @throws If divisor is zero or negative
|
|
78
|
+
*/
|
|
79
|
+
divide(divisor) {
|
|
80
|
+
if (divisor === 0n)
|
|
81
|
+
throw new Error("Division by zero");
|
|
82
|
+
if (divisor < 0n)
|
|
83
|
+
throw new Error("Divisor cannot be negative");
|
|
84
|
+
return new Amount(this.raw / divisor, this.decimals, this.symbol);
|
|
85
|
+
}
|
|
86
|
+
// ── Comparisons ──────────────────────────────────────────────
|
|
87
|
+
eq(other) {
|
|
88
|
+
this.assertSameDecimals(other);
|
|
89
|
+
return this.raw === other.raw;
|
|
90
|
+
}
|
|
91
|
+
gt(other) {
|
|
92
|
+
this.assertSameDecimals(other);
|
|
93
|
+
return this.raw > other.raw;
|
|
94
|
+
}
|
|
95
|
+
gte(other) {
|
|
96
|
+
this.assertSameDecimals(other);
|
|
97
|
+
return this.raw >= other.raw;
|
|
98
|
+
}
|
|
99
|
+
lt(other) {
|
|
100
|
+
this.assertSameDecimals(other);
|
|
101
|
+
return this.raw < other.raw;
|
|
102
|
+
}
|
|
103
|
+
lte(other) {
|
|
104
|
+
this.assertSameDecimals(other);
|
|
105
|
+
return this.raw <= other.raw;
|
|
106
|
+
}
|
|
107
|
+
isZero() {
|
|
108
|
+
return this.raw === 0n;
|
|
109
|
+
}
|
|
110
|
+
// ── Formatting ───────────────────────────────────────────────
|
|
111
|
+
/** Returns the raw bigint value (base units). */
|
|
112
|
+
toBase() {
|
|
113
|
+
return this.raw;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Returns human-readable string (e.g., "1.5").
|
|
117
|
+
* Trailing zeros after decimal are preserved to one place minimum.
|
|
118
|
+
*/
|
|
119
|
+
toUnit() {
|
|
120
|
+
const str = this.raw.toString().padStart(this.decimals + 1, "0");
|
|
121
|
+
const whole = str.slice(0, str.length - this.decimals) || "0";
|
|
122
|
+
const fraction = str.slice(str.length - this.decimals);
|
|
123
|
+
if (this.decimals === 0)
|
|
124
|
+
return whole;
|
|
125
|
+
// Trim trailing zeros but keep at least one decimal place
|
|
126
|
+
const trimmed = fraction.replace(/0+$/, "") || "0";
|
|
127
|
+
return `${whole}.${trimmed}`;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Returns formatted string with symbol (e.g., "1.5 USDC").
|
|
131
|
+
* @param compressed - If true, shorten large numbers (e.g., "1.5M USDC")
|
|
132
|
+
*/
|
|
133
|
+
toFormatted(compressed = false) {
|
|
134
|
+
if (compressed) {
|
|
135
|
+
// Use BigInt arithmetic to avoid parseFloat precision loss
|
|
136
|
+
const scale = 10n ** BigInt(this.decimals);
|
|
137
|
+
const wholePart = this.raw / scale;
|
|
138
|
+
let display;
|
|
139
|
+
if (wholePart >= 1000000n) {
|
|
140
|
+
const major = wholePart / 1000000n;
|
|
141
|
+
const minor = (wholePart % 1000000n) / 100000n; // one decimal
|
|
142
|
+
display = `${major}.${minor}M`;
|
|
143
|
+
}
|
|
144
|
+
else if (wholePart >= 1000n) {
|
|
145
|
+
const major = wholePart / 1000n;
|
|
146
|
+
const minor = (wholePart % 1000n) / 100n; // one decimal
|
|
147
|
+
display = `${major}.${minor}K`;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
display = this.toUnit();
|
|
151
|
+
}
|
|
152
|
+
return this.symbol ? `${display} ${this.symbol}` : display;
|
|
153
|
+
}
|
|
154
|
+
return this.symbol ? `${this.toUnit()} ${this.symbol}` : this.toUnit();
|
|
155
|
+
}
|
|
156
|
+
// ── Internal ─────────────────────────────────────────────────
|
|
157
|
+
assertSameDecimals(other) {
|
|
158
|
+
if (this.decimals !== other.decimals) {
|
|
159
|
+
throw new Error(`Cannot operate on amounts with different decimals: ${this.decimals} vs ${other.decimals}`);
|
|
160
|
+
}
|
|
161
|
+
if (this.symbol && other.symbol && this.symbol !== other.symbol) {
|
|
162
|
+
throw new Error(`Cannot operate on amounts with different symbols: ${this.symbol} vs ${other.symbol}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=amount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.js","sourceRoot":"","sources":["../../src/tokens/amount.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Account, Call } from "starknet";
|
|
2
|
+
import { Amount, type TokenInfo } from "./amount.js";
|
|
3
|
+
/**
|
|
4
|
+
* Typed ERC20 contract wrapper.
|
|
5
|
+
* Generates Call objects for approve/transfer and queries for balance/allowance.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const usdc = new Erc20({ symbol: 'USDC', decimals: 6, address: '0x...' }, account);
|
|
9
|
+
* const balance = await usdc.balanceOf(myAddress);
|
|
10
|
+
* console.log(balance.toFormatted()); // "150.5 USDC"
|
|
11
|
+
*/
|
|
12
|
+
export declare class Erc20 {
|
|
13
|
+
private readonly token;
|
|
14
|
+
private readonly account;
|
|
15
|
+
constructor(token: TokenInfo, account: Account);
|
|
16
|
+
/**
|
|
17
|
+
* Query the balance of an address.
|
|
18
|
+
* @returns Amount representing the balance
|
|
19
|
+
*/
|
|
20
|
+
balanceOf(address: string): Promise<Amount>;
|
|
21
|
+
/**
|
|
22
|
+
* Query the allowance of owner for spender.
|
|
23
|
+
* @returns Amount representing the allowance
|
|
24
|
+
*/
|
|
25
|
+
allowance(owner: string, spender: string): Promise<Amount>;
|
|
26
|
+
/**
|
|
27
|
+
* Build an approve Call (does not execute).
|
|
28
|
+
* @returns Call object for use with TxBuilder or Account.execute()
|
|
29
|
+
*/
|
|
30
|
+
approveCall(spender: string, amount: Amount): Call;
|
|
31
|
+
/**
|
|
32
|
+
* Build a transfer Call (does not execute).
|
|
33
|
+
* @returns Call object for use with TxBuilder or Account.execute()
|
|
34
|
+
*/
|
|
35
|
+
transferCall(recipient: string, amount: Amount): Call;
|
|
36
|
+
private assertCompatibleAmount;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=erc20.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erc20.d.ts","sourceRoot":"","sources":["../../src/tokens/erc20.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;GAQG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO;IAK9C;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWjD;;;OAGG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUhE;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAUlD;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAUrD,OAAO,CAAC,sBAAsB;CAY/B"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { uint256 } from "starknet";
|
|
2
|
+
import { Amount } from "./amount.js";
|
|
3
|
+
/**
|
|
4
|
+
* Typed ERC20 contract wrapper.
|
|
5
|
+
* Generates Call objects for approve/transfer and queries for balance/allowance.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const usdc = new Erc20({ symbol: 'USDC', decimals: 6, address: '0x...' }, account);
|
|
9
|
+
* const balance = await usdc.balanceOf(myAddress);
|
|
10
|
+
* console.log(balance.toFormatted()); // "150.5 USDC"
|
|
11
|
+
*/
|
|
12
|
+
export class Erc20 {
|
|
13
|
+
constructor(token, account) {
|
|
14
|
+
this.token = token;
|
|
15
|
+
this.account = account;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Query the balance of an address.
|
|
19
|
+
* @returns Amount representing the balance
|
|
20
|
+
*/
|
|
21
|
+
async balanceOf(address) {
|
|
22
|
+
const result = await this.account.callContract({
|
|
23
|
+
contractAddress: this.token.address,
|
|
24
|
+
entrypoint: "balanceOf",
|
|
25
|
+
calldata: [address],
|
|
26
|
+
});
|
|
27
|
+
// StarkNet returns uint256 as [low, high]
|
|
28
|
+
const raw = uint256.uint256ToBN({ low: result[0], high: result[1] });
|
|
29
|
+
return Amount.fromRaw(raw, this.token.decimals, this.token.symbol);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Query the allowance of owner for spender.
|
|
33
|
+
* @returns Amount representing the allowance
|
|
34
|
+
*/
|
|
35
|
+
async allowance(owner, spender) {
|
|
36
|
+
const result = await this.account.callContract({
|
|
37
|
+
contractAddress: this.token.address,
|
|
38
|
+
entrypoint: "allowance",
|
|
39
|
+
calldata: [owner, spender],
|
|
40
|
+
});
|
|
41
|
+
const raw = uint256.uint256ToBN({ low: result[0], high: result[1] });
|
|
42
|
+
return Amount.fromRaw(raw, this.token.decimals, this.token.symbol);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build an approve Call (does not execute).
|
|
46
|
+
* @returns Call object for use with TxBuilder or Account.execute()
|
|
47
|
+
*/
|
|
48
|
+
approveCall(spender, amount) {
|
|
49
|
+
this.assertCompatibleAmount(amount);
|
|
50
|
+
const u256 = uint256.bnToUint256(amount.toBase());
|
|
51
|
+
return {
|
|
52
|
+
contractAddress: this.token.address,
|
|
53
|
+
entrypoint: "approve",
|
|
54
|
+
calldata: [spender, u256.low.toString(), u256.high.toString()],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Build a transfer Call (does not execute).
|
|
59
|
+
* @returns Call object for use with TxBuilder or Account.execute()
|
|
60
|
+
*/
|
|
61
|
+
transferCall(recipient, amount) {
|
|
62
|
+
this.assertCompatibleAmount(amount);
|
|
63
|
+
const u256 = uint256.bnToUint256(amount.toBase());
|
|
64
|
+
return {
|
|
65
|
+
contractAddress: this.token.address,
|
|
66
|
+
entrypoint: "transfer",
|
|
67
|
+
calldata: [recipient, u256.low.toString(), u256.high.toString()],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
assertCompatibleAmount(amount) {
|
|
71
|
+
if (amount.decimals !== this.token.decimals) {
|
|
72
|
+
throw new Error(`Amount decimals mismatch: expected ${this.token.decimals}, got ${amount.decimals}`);
|
|
73
|
+
}
|
|
74
|
+
if (amount.symbol && amount.symbol !== this.token.symbol) {
|
|
75
|
+
throw new Error(`Amount symbol mismatch: expected ${this.token.symbol}, got ${amount.symbol}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=erc20.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erc20.js","sourceRoot":"","sources":["../../src/tokens/erc20.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"symbol": "ETH",
|
|
4
|
+
"decimals": 18,
|
|
5
|
+
"address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
|
|
6
|
+
"name": "Ether"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"symbol": "USDC",
|
|
10
|
+
"decimals": 6,
|
|
11
|
+
"address": "0x033068f6539f8e6e6b131e6b2b814e6c34a5224bc66947c47dab9dfee93b35fb",
|
|
12
|
+
"name": "USD Coin"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"symbol": "USDT",
|
|
16
|
+
"decimals": 6,
|
|
17
|
+
"address": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
|
|
18
|
+
"name": "Tether USD"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"symbol": "DAI",
|
|
22
|
+
"decimals": 18,
|
|
23
|
+
"address": "0x00da114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3",
|
|
24
|
+
"name": "Dai Stablecoin"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"symbol": "STRK",
|
|
28
|
+
"decimals": 18,
|
|
29
|
+
"address": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
|
|
30
|
+
"name": "Starknet Token"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"symbol": "WBTC",
|
|
34
|
+
"decimals": 8,
|
|
35
|
+
"address": "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
|
|
36
|
+
"name": "Wrapped Bitcoin"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"symbol": "ETH",
|
|
4
|
+
"decimals": 18,
|
|
5
|
+
"address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
|
|
6
|
+
"name": "Ether"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"symbol": "USDC",
|
|
10
|
+
"decimals": 6,
|
|
11
|
+
"address": "0x005a643907b9a4bc6a55e9069c4fd5fd1f5c79a22470690f75556c4736e34426",
|
|
12
|
+
"name": "USD Coin (Testnet)"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"symbol": "STRK",
|
|
16
|
+
"decimals": 18,
|
|
17
|
+
"address": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
|
|
18
|
+
"name": "Starknet Token"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TokenInfo } from "./amount.js";
|
|
2
|
+
export type Network = "mainnet" | "sepolia";
|
|
3
|
+
interface TokenEntry extends TokenInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Registry for looking up token metadata by symbol or address.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const registry = new TokenRegistry('mainnet');
|
|
11
|
+
* const usdc = registry.bySymbol('USDC');
|
|
12
|
+
* const amount = Amount.parse('10.0', usdc!);
|
|
13
|
+
*/
|
|
14
|
+
export declare class TokenRegistry {
|
|
15
|
+
private readonly tokens;
|
|
16
|
+
private readonly bySymbolMap;
|
|
17
|
+
private readonly byAddressMap;
|
|
18
|
+
constructor(network: Network);
|
|
19
|
+
/** Find a token by its symbol (case-insensitive). */
|
|
20
|
+
bySymbol(symbol: string): TokenEntry | undefined;
|
|
21
|
+
/** Find a token by its contract address (case-insensitive). */
|
|
22
|
+
byAddress(address: string): TokenEntry | undefined;
|
|
23
|
+
/** List all tokens in this registry. */
|
|
24
|
+
all(): TokenEntry[];
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tokens/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAE5C,UAAU,UAAW,SAAQ,SAAS;IACpC,IAAI,EAAE,MAAM,CAAC;CACd;AAOD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA0B;IACtD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0B;gBAE3C,OAAO,EAAE,OAAO;IAU5B,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIhD,+DAA+D;IAC/D,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIlD,wCAAwC;IACxC,GAAG,IAAI,UAAU,EAAE;CAGpB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import mainnetTokens from "./presets/mainnet.json" with { type: "json" };
|
|
2
|
+
import sepoliaTokens from "./presets/sepolia.json" with { type: "json" };
|
|
3
|
+
const PRESETS = {
|
|
4
|
+
mainnet: mainnetTokens,
|
|
5
|
+
sepolia: sepoliaTokens,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Registry for looking up token metadata by symbol or address.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const registry = new TokenRegistry('mainnet');
|
|
12
|
+
* const usdc = registry.bySymbol('USDC');
|
|
13
|
+
* const amount = Amount.parse('10.0', usdc!);
|
|
14
|
+
*/
|
|
15
|
+
export class TokenRegistry {
|
|
16
|
+
constructor(network) {
|
|
17
|
+
const preset = PRESETS[network];
|
|
18
|
+
if (!preset) {
|
|
19
|
+
throw new Error(`Invalid network: "${network}". Must be "mainnet" or "sepolia".`);
|
|
20
|
+
}
|
|
21
|
+
this.tokens = preset;
|
|
22
|
+
this.bySymbolMap = new Map(preset.map((t) => [t.symbol.toUpperCase(), t]));
|
|
23
|
+
this.byAddressMap = new Map(preset.map((t) => [t.address.toLowerCase(), t]));
|
|
24
|
+
}
|
|
25
|
+
/** Find a token by its symbol (case-insensitive). */
|
|
26
|
+
bySymbol(symbol) {
|
|
27
|
+
return this.bySymbolMap.get(symbol.toUpperCase());
|
|
28
|
+
}
|
|
29
|
+
/** Find a token by its contract address (case-insensitive). */
|
|
30
|
+
byAddress(address) {
|
|
31
|
+
return this.byAddressMap.get(address.toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
/** List all tokens in this registry. */
|
|
34
|
+
all() {
|
|
35
|
+
return [...this.tokens];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tokens/registry.ts"],"names":[],"mappings":"AACA,OAAO,aAAa,MAAM,wBAAwB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACzE,OAAO,aAAa,MAAM,wBAAwB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAQzE,MAAM,OAAO,GAAkC;IAC7C,OAAO,EAAE,aAA6B;IACtC,OAAO,EAAE,aAA6B;CACvC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,OAAO,aAAa;IAKxB,YAAY,OAAgB;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,qBAAqB,OAAO,oCAAoC,CAAC,CAAC;QACpF,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,CAAC;IAED,qDAAqD;IACrD,QAAQ,CAAC,MAAc;QACrB,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,+DAA+D;IAC/D,SAAS,CAAC,OAAe;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,wCAAwC;IACxC,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Account, Call, InvokeFunctionResponse, EstimateFeeResponseOverhead } from "starknet";
|
|
2
|
+
import type { TransferTarget, PreflightResult } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Fluent builder for batching Starknet calls.
|
|
5
|
+
* All calls execute atomically via Account.execute().
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const result = await new TxBuilder(account)
|
|
9
|
+
* .approve(tokenAddr, spenderAddr, amount)
|
|
10
|
+
* .transfer(tokenAddr, [{ to: recipient, amount: 1000n }])
|
|
11
|
+
* .send();
|
|
12
|
+
*/
|
|
13
|
+
export declare class TxBuilder {
|
|
14
|
+
private readonly account;
|
|
15
|
+
private readonly _calls;
|
|
16
|
+
private sent;
|
|
17
|
+
constructor(account: Account);
|
|
18
|
+
/** Returns a copy of the current call batch. */
|
|
19
|
+
calls(): Call[];
|
|
20
|
+
/**
|
|
21
|
+
* Add one or more raw calls.
|
|
22
|
+
* @param call - A single Call or array of Calls
|
|
23
|
+
*/
|
|
24
|
+
add(call: Call | Call[]): this;
|
|
25
|
+
/**
|
|
26
|
+
* Add an ERC20 approve call.
|
|
27
|
+
* @param token - Token contract address
|
|
28
|
+
* @param spender - Address to approve
|
|
29
|
+
* @param amount - Raw amount in base units
|
|
30
|
+
*/
|
|
31
|
+
approve(token: string, spender: string, amount: bigint): this;
|
|
32
|
+
/**
|
|
33
|
+
* Add one or more ERC20 transfer calls.
|
|
34
|
+
* @param token - Token contract address
|
|
35
|
+
* @param targets - Array of { to, amount } pairs
|
|
36
|
+
*/
|
|
37
|
+
transfer(token: string, targets: TransferTarget[]): this;
|
|
38
|
+
/**
|
|
39
|
+
* Execute all batched calls atomically.
|
|
40
|
+
* @throws If no calls have been added
|
|
41
|
+
* @throws If send() was already called
|
|
42
|
+
*/
|
|
43
|
+
send(): Promise<InvokeFunctionResponse>;
|
|
44
|
+
/**
|
|
45
|
+
* Estimate fees for the current batch without executing.
|
|
46
|
+
* @throws If no calls have been added
|
|
47
|
+
*/
|
|
48
|
+
estimateFee(): Promise<EstimateFeeResponseOverhead>;
|
|
49
|
+
/**
|
|
50
|
+
* Simulate the transaction without submitting to the network.
|
|
51
|
+
* Useful for checking if a transaction would succeed.
|
|
52
|
+
* @throws If no calls have been added
|
|
53
|
+
*/
|
|
54
|
+
preflight(): Promise<PreflightResult>;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=tx-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tx-builder.d.ts","sourceRoot":"","sources":["../../src/transactions/tx-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAEnG,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElE;;;;;;;;;GASG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,IAAI,CAAS;gBAET,OAAO,EAAE,OAAO;IAI5B,gDAAgD;IAChD,KAAK,IAAI,IAAI,EAAE;IAIf;;;OAGG;IACH,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI;IAM9B;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAW7D;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI;IAaxD;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAQ7C;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,2BAA2B,CAAC;IAKzD;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;CAW5C"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { uint256 } from "starknet";
|
|
2
|
+
/**
|
|
3
|
+
* Fluent builder for batching Starknet calls.
|
|
4
|
+
* All calls execute atomically via Account.execute().
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const result = await new TxBuilder(account)
|
|
8
|
+
* .approve(tokenAddr, spenderAddr, amount)
|
|
9
|
+
* .transfer(tokenAddr, [{ to: recipient, amount: 1000n }])
|
|
10
|
+
* .send();
|
|
11
|
+
*/
|
|
12
|
+
export class TxBuilder {
|
|
13
|
+
constructor(account) {
|
|
14
|
+
this._calls = [];
|
|
15
|
+
this.sent = false;
|
|
16
|
+
this.account = account;
|
|
17
|
+
}
|
|
18
|
+
/** Returns a copy of the current call batch. */
|
|
19
|
+
calls() {
|
|
20
|
+
return [...this._calls];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Add one or more raw calls.
|
|
24
|
+
* @param call - A single Call or array of Calls
|
|
25
|
+
*/
|
|
26
|
+
add(call) {
|
|
27
|
+
const toAdd = Array.isArray(call) ? call : [call];
|
|
28
|
+
this._calls.push(...toAdd);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add an ERC20 approve call.
|
|
33
|
+
* @param token - Token contract address
|
|
34
|
+
* @param spender - Address to approve
|
|
35
|
+
* @param amount - Raw amount in base units
|
|
36
|
+
*/
|
|
37
|
+
approve(token, spender, amount) {
|
|
38
|
+
if (amount < 0n)
|
|
39
|
+
throw new RangeError("Approve amount cannot be negative");
|
|
40
|
+
const u256 = uint256.bnToUint256(amount);
|
|
41
|
+
this._calls.push({
|
|
42
|
+
contractAddress: token,
|
|
43
|
+
entrypoint: "approve",
|
|
44
|
+
calldata: [spender, u256.low.toString(), u256.high.toString()],
|
|
45
|
+
});
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Add one or more ERC20 transfer calls.
|
|
50
|
+
* @param token - Token contract address
|
|
51
|
+
* @param targets - Array of { to, amount } pairs
|
|
52
|
+
*/
|
|
53
|
+
transfer(token, targets) {
|
|
54
|
+
for (const { to, amount } of targets) {
|
|
55
|
+
if (amount < 0n)
|
|
56
|
+
throw new RangeError("Transfer amount cannot be negative");
|
|
57
|
+
const u256 = uint256.bnToUint256(amount);
|
|
58
|
+
this._calls.push({
|
|
59
|
+
contractAddress: token,
|
|
60
|
+
entrypoint: "transfer",
|
|
61
|
+
calldata: [to, u256.low.toString(), u256.high.toString()],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute all batched calls atomically.
|
|
68
|
+
* @throws If no calls have been added
|
|
69
|
+
* @throws If send() was already called
|
|
70
|
+
*/
|
|
71
|
+
async send() {
|
|
72
|
+
if (this.sent)
|
|
73
|
+
throw new Error("TxBuilder already sent — create a new builder");
|
|
74
|
+
if (this._calls.length === 0)
|
|
75
|
+
throw new Error("No calls to execute");
|
|
76
|
+
this.sent = true;
|
|
77
|
+
const calls = [...this._calls];
|
|
78
|
+
return this.account.execute(calls);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Estimate fees for the current batch without executing.
|
|
82
|
+
* @throws If no calls have been added
|
|
83
|
+
*/
|
|
84
|
+
async estimateFee() {
|
|
85
|
+
if (this._calls.length === 0)
|
|
86
|
+
throw new Error("No calls to estimate");
|
|
87
|
+
return this.account.estimateInvokeFee([...this._calls]);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Simulate the transaction without submitting to the network.
|
|
91
|
+
* Useful for checking if a transaction would succeed.
|
|
92
|
+
* @throws If no calls have been added
|
|
93
|
+
*/
|
|
94
|
+
async preflight() {
|
|
95
|
+
if (this._calls.length === 0)
|
|
96
|
+
throw new Error("No calls to preflight");
|
|
97
|
+
const result = await this.account.simulateTransaction([{ type: "INVOKE", payload: [...this._calls] }], { skipValidate: false });
|
|
98
|
+
if (!result || result.length === 0) {
|
|
99
|
+
throw new Error("Simulation returned no results");
|
|
100
|
+
}
|
|
101
|
+
return result[0];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=tx-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tx-builder.js","sourceRoot":"","sources":["../../src/transactions/tx-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transactions/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transactions/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chipi-stack/core",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Core primitives for Chipi Pay SDK: TxBuilder, Amount, SignerAdapter, TokenRegistry",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@chipi-stack/types": "^14.0.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.7.3",
|
|
22
|
+
"vitest": "^3.2.4",
|
|
23
|
+
"starknet": ">=6.23.1"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"starknet": ">=6.23.1"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.17.0"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"typecheck": "tsc --noEmit"
|
|
40
|
+
}
|
|
41
|
+
}
|