/
bid.js
210 lines (177 loc) · 6.74 KB
/
bid.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
const abi = require("../abi.js");
const env = require("hardhat");
const { ethers } = require("ethers");
const {
arrayify,
hexlify,
SigningKey,
keccak256,
recoverPublicKey,
computeAddress,
} = require("ethers/lib/utils");
// Dynamically import the ES Modules and return them
async function loadESModules() {
try {
const neutrino = await import("@solar-republic/neutrino");
const belt = await import("@blake.regalia/belt");
return { neutrino, belt };
} catch (error) {
console.error("Failed to load ES modules:", error);
return null;
}
}
let bid_tx = async () => {
const modules = await loadESModules();
if (!modules) {
console.log("Required modules could not be loaded.");
return;
}
const { ecdh, chacha20_poly1305_seal } = modules.neutrino;
const {
bytes,
bytes_to_base64,
json_to_bytes,
sha256,
concat,
text_to_bytes,
base64_to_bytes,
} = modules.belt;
//EVM gateway contract address
const publicClientAddress = "0x3879E146140b627a5C858a08e507B171D9E43139";
const routing_contract = process.env.SECRET_ADDRESS;
const routing_code_hash = process.env.CODE_HASH;
const iface = new ethers.utils.Interface(abi);
const privateKey = process.env.PRIVATE_KEY;
const provider = new ethers.providers.JsonRpcProvider(process.env.INFURA);
// Create a wallet instance from a private key
const my_wallet = new ethers.Wallet(privateKey, provider);
// Generating ephemeral keys
const wallet = ethers.Wallet.createRandom();
const userPrivateKeyBytes = arrayify(wallet.privateKey);
const userPublicKey = new SigningKey(wallet.privateKey).compressedPublicKey;
const userPublicKeyBytes = arrayify(userPublicKey);
// Gateway Encryption key for ChaCha20-Poly1305 Payload encryption
const gatewayPublicKey = "A20KrD7xDmkFXpNMqJn1CLpRaDLcdKpO1NdBBS7VpWh3";
const gatewayPublicKeyBytes = base64_to_bytes(gatewayPublicKey);
// create the sharedKey via ECDH
const sharedKey = await sha256(
ecdh(userPrivateKeyBytes, gatewayPublicKeyBytes)
);
const myAddress = my_wallet.address;
const amount = "300";
const bidder_address = myAddress;
const index = "1";
const callback_gas_limit = 300000;
//the function name of the function that is called on the private contract
const handle = "create_bid";
const data = JSON.stringify({
amount,
bidder_address,
index,
});
const callbackAddress = publicClientAddress.toLowerCase();
//This is an empty callback for the sake of having a callback in the sample code.
//Here, you would put your callback selector for you contract in.
const callbackSelector = iface.getSighash(
iface.getFunction("upgradeHandler")
);
const callbackGasLimit = Number(callback_gas_limit);
//payload data that are going to be encrypted
const payload = {
data: data,
routing_info: routing_contract,
routing_code_hash: routing_code_hash,
user_address: myAddress,
user_key: bytes_to_base64(userPublicKeyBytes),
callback_address: bytes_to_base64(arrayify(callbackAddress)),
callback_selector: bytes_to_base64(arrayify(callbackSelector)),
callback_gas_limit: callbackGasLimit,
};
//build a Json of the payload
const payloadJson = JSON.stringify(payload);
const plaintext = json_to_bytes(payload);
//generate a nonce for ChaCha20-Poly1305 encryption
//DO NOT skip this, stream cipher encryptions are only secure with a random nonce!
const nonce = crypto.getRandomValues(bytes(12));
//Encrypt the payload using ChachaPoly1305 and concat the ciphertext+tag to fit the Rust ChaChaPoly1305 requirements
const [ciphertextClient, tagClient] = chacha20_poly1305_seal(
sharedKey,
nonce,
plaintext
);
const ciphertext = concat([ciphertextClient, tagClient]);
//get Metamask to sign the payloadhash with personal_sign
const ciphertextHash = keccak256(ciphertext);
//this is what metamask really signs with personal_sign, it prepends the ethereum signed message here
const payloadHash = keccak256(
concat([
text_to_bytes("\x19Ethereum Signed Message:\n32"),
arrayify(ciphertextHash),
])
);
//this is what we provide to metamask
const msgParams = ciphertextHash;
const from = myAddress;
const params = [from, msgParams];
const method = "personal_sign";
console.log(`Payload Hash: ${payloadHash}`);
//
const messageArrayified = ethers.utils.arrayify(ciphertextHash); // Convert the hash to a Uint8Array
const payloadSignature = await my_wallet.signMessage(messageArrayified); // Sign the message directly with the wallet
console.log(`Payload Signature: ${payloadSignature}`);
// Continue with the rest of your original code for public key recovery and address computation
const user_pubkey = recoverPublicKey(payloadHash, payloadSignature);
console.log(`Recovered public key: ${user_pubkey}`);
console.log(
`Verify this matches the user address: ${computeAddress(user_pubkey)}`
);
// function data to be abi encoded
const _userAddress = myAddress;
const _routingInfo = routing_contract;
const _payloadHash = payloadHash;
const _info = {
user_key: hexlify(userPublicKeyBytes),
user_pubkey: user_pubkey,
routing_code_hash: routing_code_hash,
task_destination_network: "pulsar-3", //Destination for the task, here: pulsar-3 testnet
handle: handle,
nonce: hexlify(nonce),
payload: hexlify(ciphertext),
payload_signature: payloadSignature,
callback_gas_limit: Number(callbackGasLimit),
};
console.log(`_userAddress: ${_userAddress}
_routingInfo: ${_routingInfo}
_payloadHash: ${_payloadHash}
_info: ${JSON.stringify(_info)}
_callbackAddress: ${callbackAddress},
_callbackSelector: ${callbackSelector},
_callbackGasLimit: ${callbackGasLimit}`);
const functionData = iface.encodeFunctionData("send", [
_payloadHash,
_userAddress,
_routingInfo,
_info,
]);
const gasFee = await provider.getGasPrice();
const amountOfGas = gasFee.mul(callbackGasLimit).mul(3).div(2);
// Define the transaction object
const tx = {
gasLimit: ethers.utils.hexlify(150000), // Use hexlify to ensure the gas limit is correctly formatted
to: publicClientAddress,
// from: myAddress, // This is not needed because the wallet knows the 'from' address
value: ethers.utils.hexlify(amountOfGas), // Make sure to hexlify the amount
data: functionData,
};
// Send the transaction using the wallet's sendTransaction method
try {
const txResponse = await my_wallet.sendTransaction(tx);
console.log(`Transaction sent! Hash: ${txResponse.hash}`);
// Wait for the transaction to be mined
const receipt = await txResponse.wait();
console.log(`Transaction confirmed! Block Number: ${receipt.blockNumber}`);
} catch (error) {
console.error(`Error sending transaction: ${error}`);
}
};
bid_tx();