@inco/js 0.1.32 → 0.1.34
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/README.md +28 -57
- package/dist/lite/reencrypt.d.ts +1 -0
- package/dist/lite/reencrypt.js +21 -2
- package/package.json +2 -2
- package/dist/encryption/index.cjs +0 -24706
- package/dist/encryption/index.mjs +0 -25787
- package/dist/fhevm/index.cjs +0 -87
- package/dist/fhevm/index.mjs +0 -90
- package/dist/generated/abis/index.cjs +0 -12493
- package/dist/generated/abis/index.mjs +0 -12496
- package/dist/index.cjs +0 -24883
- package/dist/index.mjs +0 -25964
- package/dist/lite/index.cjs +0 -52789
- package/dist/lite/index.mjs +0 -71691
- package/dist/local/index.cjs +0 -24479
- package/dist/local/index.mjs +0 -42864
- package/dist/reencryption/index.cjs +0 -24681
- package/dist/reencryption/index.mjs +0 -24684
package/README.md
CHANGED
@@ -4,7 +4,7 @@ The @inco/js package contains the TypeScript SDK for creating dapps built on Inc
|
|
4
4
|
|
5
5
|
## Current Status: Active Development
|
6
6
|
|
7
|
-
The SDK is currently going through active development, to support all new features that Inco offers. As such, do expect breaking changes in subsequent releases, which will be documented in the [CHANGELOG](./CHANGELOG.md).
|
7
|
+
The SDK is currently going through active development, to support all new features that Inco offers. As such, do expect breaking changes in subsequent releases, which will be documented in the [CHANGELOG](./CHANGELOG.md).
|
8
8
|
|
9
9
|
## Install
|
10
10
|
|
@@ -29,76 +29,47 @@ A typical usage of `@inco/js` includes 3 steps:
|
|
29
29
|
### 1. Encrypt a value
|
30
30
|
|
31
31
|
```ts
|
32
|
-
import {
|
33
|
-
import {
|
34
|
-
import {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
} from '@inco/js/lite';
|
40
|
-
import { createWalletClient, hexToBytes } from 'viem';
|
41
|
-
|
42
|
-
// Setup. Do it once at initialization.
|
43
|
-
const chain = getSupportedChain('baseSepolia');
|
44
|
-
const lightningConfig = getActiveLightningDeployment(chain);
|
32
|
+
import { getViemChain, supportedChains } from '@inco/js';
|
33
|
+
import { Lightning } from '@inco/js/lite';
|
34
|
+
import { createWalletClient } from 'viem';
|
35
|
+
|
36
|
+
// Setup: do it once at initialization
|
37
|
+
const chainId = supportedChains.baseSepolia;
|
38
|
+
const zap = Lightning.latest('testnet', chainId); // Connect to Inco's latest public testnet
|
45
39
|
const walletClient = createWalletClient({
|
46
|
-
|
47
|
-
|
48
|
-
|
40
|
+
chain: getViemChain(chainId),
|
41
|
+
account: /* Choose your account, e.g. from window.ethereum */,
|
42
|
+
transport: /* Choose your transport, e.g. from Alchemy */,
|
49
43
|
});
|
50
|
-
const dappAddress = '
|
51
|
-
|
52
|
-
//
|
53
|
-
const plaintext =
|
54
|
-
const
|
55
|
-
|
56
|
-
|
57
|
-
value: plaintext,
|
58
|
-
type: handleTypes.euint256,
|
59
|
-
},
|
60
|
-
context: {
|
61
|
-
hostChainId: BigInt(chain.id),
|
62
|
-
aclAddress: lightningConfig.executorAddress,
|
63
|
-
userAddress: walletClient.account.address,
|
64
|
-
contractAddress: dappAddress,
|
65
|
-
},
|
66
|
-
};
|
67
|
-
|
68
|
-
// Encrypt.
|
69
|
-
const ephemeralKeypair = await generateSecp256k1Keypair();
|
70
|
-
const eciesPubKey = decodeSecp256k1PublicKey(hexToBytes(lightningConfig.eciesPublicKey));
|
71
|
-
const encryptor = getEciesEncryptor({
|
72
|
-
scheme: encryptionSchemes.ecies,
|
73
|
-
pubKeyA: eciesPubKey,
|
74
|
-
privKeyB: ephemeralKeypair,
|
44
|
+
const dappAddress = '0x00000000000000000000000000000000deadbeef'; // Put your contract address here
|
45
|
+
|
46
|
+
// Encrypt the plaintext value
|
47
|
+
const plaintext = 42;
|
48
|
+
const ciphertext = await zap.encrypt(plaintext, {
|
49
|
+
accountAddress: walletClient.account.address,
|
50
|
+
dappAddress,
|
75
51
|
});
|
76
|
-
|
77
|
-
console.log(ciphertext
|
52
|
+
|
53
|
+
console.log(ciphertext); // A long hex string representing the encrypted value
|
78
54
|
```
|
79
55
|
|
80
56
|
### 2. Post the ciphertext to the contract
|
81
57
|
|
82
|
-
|
58
|
+
This step does not require any specific `@inco/js` functionality. We recommend using [viem](https://viem.sh) to interact with the blockchain. Specifically, use the [`writeContract`](https://viem.sh/docs/contract/writeContract) method to submit transactions. Pass the `ciphertext` from the previous encryption step as the input ciphertext parameter of type `bytes`.
|
83
59
|
|
84
60
|
### 3. Request a reencryption
|
85
61
|
|
86
|
-
|
62
|
+
Following transaction submission, the Inco covalidator processes the computation request. The contract stores the computation result as a handle on-chain. This handle, referenced below as `resultHandle`, is a hexadecimal string of type `Handle` that serves as a reference to the encrypted computation output.
|
87
63
|
|
88
64
|
```ts
|
89
|
-
import { incoLiteReencryptor } from "@inco/js/lite";
|
90
65
|
import { Hex } from "viem";
|
91
66
|
|
92
|
-
//
|
93
|
-
|
94
|
-
const reencryptor = await
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
const resultPlaintext = await reencryptor({
|
99
|
-
handle: resultHandle,
|
100
|
-
});
|
101
|
-
console.log(resultPlaintext.value); // Plaintext of the final result after computation
|
67
|
+
// Request a re-encryption of the result ciphertext
|
68
|
+
const resultHandle = "0x..." as Hex; // Retrieve the handle from the contract, e.g. using viem
|
69
|
+
const reencryptor = await zap.getReencryptor(walletClient); // Use same walletClient as previous step
|
70
|
+
const resultPlaintext = await reencryptor({ handle: resultHandle });
|
71
|
+
|
72
|
+
console.log(resultPlaintext.value); // The decrypted value
|
102
73
|
```
|
103
74
|
|
104
75
|
## License
|
package/dist/lite/reencrypt.d.ts
CHANGED
@@ -21,3 +21,4 @@ export declare function defaultCovalidatorGrpc(chain: SupportedChain): string;
|
|
21
21
|
export declare function pulumiCovalidatorGrpc(chain: SupportedChain): string;
|
22
22
|
export declare function lightningDevnetCovalidatorGrpc(chain: SupportedChain): string;
|
23
23
|
export declare function lightningTestnetCovalidatorGrpc(chain: SupportedChain): string;
|
24
|
+
export declare function retryWithBackoff<T>(fn: () => Promise<T>, maxRetries?: number, baseDelayInMs?: number): Promise<T>;
|
package/dist/lite/reencrypt.js
CHANGED
@@ -68,7 +68,7 @@ export async function incoLiteReencryptor({ kmsConnectRpcEndpointOrClient, chain
|
|
68
68
|
],
|
69
69
|
};
|
70
70
|
try {
|
71
|
-
const response = await kmsClient.reencrypt(reencryptRequest);
|
71
|
+
const response = await retryWithBackoff(() => kmsClient.reencrypt(reencryptRequest));
|
72
72
|
return decryptGrpcResponse(response, ephemeralKeypair, handle);
|
73
73
|
}
|
74
74
|
catch (error) {
|
@@ -126,4 +126,23 @@ function camelToDashCase(str) {
|
|
126
126
|
function getCovalidatorGrpcHelper(chain, network, cluster) {
|
127
127
|
return `https://grpc.${camelToDashCase(chain.name)}.${cluster}.${network}.inco.org`;
|
128
128
|
}
|
129
|
-
|
129
|
+
// Helper function to implement exponential backoff retry logic
|
130
|
+
export async function retryWithBackoff(fn, maxRetries = 3, baseDelayInMs = 1000) {
|
131
|
+
let lastError;
|
132
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
133
|
+
try {
|
134
|
+
return await fn();
|
135
|
+
}
|
136
|
+
catch (error) {
|
137
|
+
lastError = error;
|
138
|
+
if (attempt === maxRetries - 1) {
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
const delay = baseDelayInMs * Math.pow(2, attempt);
|
142
|
+
const jitter = delay * (0.8 + Math.random() * 0.4); // random jitter, 80% to 120% of the delay
|
143
|
+
await new Promise((resolve) => setTimeout(resolve, jitter));
|
144
|
+
}
|
145
|
+
}
|
146
|
+
throw lastError;
|
147
|
+
}
|
148
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVlbmNyeXB0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpdGUvcmVlbmNyeXB0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBVSxZQUFZLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNqRSxPQUFPLEVBQVcsVUFBVSxFQUFjLFVBQVUsRUFBMkIsTUFBTSxNQUFNLENBQUM7QUFDNUYsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUMxQyxPQUFPLEVBQUUsaUJBQWlCLEVBQWtCLE1BQU0sVUFBVSxDQUFDO0FBQzdELE9BQU8sRUFBRSxpQkFBaUIsRUFBZSxpQkFBaUIsRUFBb0IsTUFBTSwwQkFBMEIsQ0FBQztBQUMvRyxPQUFPLEVBQUUsVUFBVSxFQUF1QyxNQUFNLGlEQUFpRCxDQUFDO0FBQ2xILE9BQU8sRUFBRSxhQUFhLEVBQVUsTUFBTSxXQUFXLENBQUM7QUFFbEQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDN0QsT0FBTyxFQUFFLE9BQU8sRUFBRSx3QkFBd0IsRUFBb0IsTUFBTSxTQUFTLENBQUM7QUFpQjlFLGtEQUFrRDtBQUNsRCw4RUFBOEU7QUFDOUUsK0VBQStFO0FBQy9FLGdEQUFnRDtBQUNoRCxNQUFNLHNCQUFzQixHQUFHO0lBQzdCLElBQUksRUFBRSxrQkFBa0I7SUFDeEIsT0FBTyxFQUFFLE9BQU87Q0FDakIsQ0FBQztBQUVGLDhEQUE4RDtBQUM5RCxNQUFNLFVBQVUsZUFBZSxDQUFDLE9BQWUsRUFBRSxlQUEyQjtJQUMxRSxPQUFPLG1CQUFtQixDQUFDO1FBQ3pCLE9BQU87UUFDUCxXQUFXLEVBQUUscUJBQXFCO1FBQ2xDLGlCQUFpQixFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUN6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
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@inco/js",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.34",
|
4
4
|
"repository": "https://github.com/Inco-fhevm/inco-monorepo",
|
5
5
|
"scripts": {
|
6
6
|
"test": "bun run lint && bun run test:tsc && bun run test:unit",
|
@@ -12,7 +12,7 @@
|
|
12
12
|
"build:declarations": "tsc --emitDeclarationOnly --project tsconfig.dist.json",
|
13
13
|
"build:tsc": "tsc --project tsconfig.dist.json",
|
14
14
|
"build": "bun run build:generate && bun run build:tsc",
|
15
|
-
"build:generate": "bun run generate && bun run
|
15
|
+
"build:generate": "bun run generate && bun run generate:protos && bun run generate:abis",
|
16
16
|
"generate:protos": "buf generate && buf generate --template buf.gen.ts-proto.yaml",
|
17
17
|
"generate:abis": "wagmi generate",
|
18
18
|
"generate:local": "bun generate.ts",
|