diff --git a/projects/messenger/.gitignore b/projects/messenger/.gitignore index 40b878d..92bfe37 100644 --- a/projects/messenger/.gitignore +++ b/projects/messenger/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +xdapp.config.json diff --git a/projects/messenger/chains/algorand/messenger.py b/projects/messenger/chains/algorand/messenger.py new file mode 100644 index 0000000..5bc669e --- /dev/null +++ b/projects/messenger/chains/algorand/messenger.py @@ -0,0 +1,318 @@ +#!/usr/bin/python3 +""" +Copyright 2022 Wormhole Project Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +""" + +from algosdk import account, mnemonic, abi +from algosdk.encoding import decode_address, encode_address +from algosdk.future import transaction +from algosdk.logic import get_application_address +from algosdk.v2client.algod import AlgodClient +from base64 import b64decode +from pyteal.ast import * +from pyteal.compiler import * +from pyteal.ir import * +from pyteal.types import * +from typing import List, Tuple, Dict, Any, Optional, Union +import pprint +import sys + +class PendingTxnResponse: + def __init__(self, response: Dict[str, Any]) -> None: + self.poolError: str = response["pool-error"] + self.txn: Dict[str, Any] = response["txn"] + + self.applicationIndex: Optional[int] = response.get("application-index") + self.assetIndex: Optional[int] = response.get("asset-index") + self.closeRewards: Optional[int] = response.get("close-rewards") + self.closingAmount: Optional[int] = response.get("closing-amount") + self.confirmedRound: Optional[int] = response.get("confirmed-round") + self.globalStateDelta: Optional[Any] = response.get("global-state-delta") + self.localStateDelta: Optional[Any] = response.get("local-state-delta") + self.receiverRewards: Optional[int] = response.get("receiver-rewards") + self.senderRewards: Optional[int] = response.get("sender-rewards") + + self.innerTxns: List[Any] = response.get("inner-txns", []) + self.logs: List[bytes] = [b64decode(l) for l in response.get("logs", [])] + +class Account: + """Represents a private key and address for an Algorand account""" + + def __init__(self, privateKey: str) -> None: + self.sk = privateKey + self.addr = account.address_from_private_key(privateKey) + print (privateKey) + print (" " + self.getMnemonic()) + print (" " + self.addr) + + def getAddress(self) -> str: + return self.addr + + def getPrivateKey(self) -> str: + return self.sk + + def getMnemonic(self) -> str: + return mnemonic.from_private_key(self.sk) + + @classmethod + def FromMnemonic(cls, m: str) -> "Account": + return cls(mnemonic.to_private_key(m)) + + +def fullyCompileContract(client: AlgodClient, contract: Expr) -> bytes: + teal = compileTeal(contract, mode=Mode.Application, version=6) + response = client.compile(teal) + return response + +def clear_app(): + return Int(1) + +devMode = True + +def approve_app(): + me = Global.current_application_address() + + # This bit of magic causes the line number of the assert to show + # up in the small bit of info shown when an assert trips. This + # tells you the actual line number of the assert that caused the + # txn to fail. + def MagicAssert(a) -> Expr: + if devMode: + from inspect import currentframe + return Assert(And(a, Int(currentframe().f_back.f_lineno))) + else: + return Assert(a) + + # potential badness + def assert_common_checks(e) -> Expr: + return MagicAssert(And( + e.rekey_to() == Global.zero_address(), + e.close_remainder_to() == Global.zero_address(), + e.asset_close_to() == Global.zero_address() + )) + + def sendMessage(): + return Seq( + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.ApplicationCall, + TxnField.application_id: App.globalGet(Bytes("coreid")), + TxnField.application_args: [Bytes("publishMessage"), Txn.application_args[1], Itob(Int(0))], + TxnField.accounts: [Txn.accounts[1]], + TxnField.note: Bytes("publishMessage"), + TxnField.fee: Int(0), + } + ), + InnerTxnBuilder.Submit(), + # It is the way... + Approve() + ) + + @Subroutine(TealType.uint64) + def checkedGet(v) -> Expr: + maybe = App.globalGetEx(Txn.application_id(), v) + # If we assert here, it means we have not registered the emitter + return Seq(maybe, MagicAssert(maybe.hasValue()), maybe.value()) + + def receiveMessage(): + off = ScratchVar() + emitter = ScratchVar() + sequence = ScratchVar() + tidx = ScratchVar() + + return Seq([ + # First, lets make sure we are looking at the correct vaa version... + MagicAssert(Btoi(Extract(Txn.application_args[1], Int(0), Int(1))) == Int(1)), + + # From the vaa, I will grab the emitter and sequence number + off.store(Btoi(Extract(Txn.application_args[1], Int(5), Int(1))) * Int(66) + Int(14)), # The offset of the chain/emitter + emitter.store(Extract(Txn.application_args[1], off.load(), Int(34))), + sequence.store(Btoi(Extract(Txn.application_args[1], off.load() + Int(34), Int(8)))), + + # Should be going up and never repeating.. If you want + # something that can take messages in any order, look at + # checkForDuplicate() in the token_bridge contract. It is + # kind of a heavy lift but it can be done + MagicAssert(sequence.load() > checkedGet(emitter.load())), + App.globalPut(emitter.load(), sequence.load()), + + # Now lets check to see if this vaa was actually signed by + # the guardians. We do this by confirming that the + # previous txn in the group was to the wormhole core and + # against the verifyVAA method. If that passed, then the + # vaa must be legit + MagicAssert(Txn.group_index() > Int(0)), + tidx.store(Txn.group_index() - Int(1)), + MagicAssert(And( + # Lets see if the vaa we are about to process was actually verified by the core + Gtxn[tidx.load()].type_enum() == TxnType.ApplicationCall, + Gtxn[tidx.load()].application_id() == App.globalGet(Bytes("coreid")), + Gtxn[tidx.load()].application_args[0] == Bytes("verifyVAA"), + Gtxn[tidx.load()].sender() == Txn.sender(), + + # we are all taking about the same vaa? + Gtxn[tidx.load()].application_args[1] == Txn.application_args[1], + + # We all opted into the same accounts? + Gtxn[tidx.load()].accounts[0] == Txn.accounts[0], + Gtxn[tidx.load()].accounts[1] == Txn.accounts[1], + Gtxn[tidx.load()].accounts[2] == Txn.accounts[2], + )), + + # check for hackery + assert_common_checks(Gtxn[tidx.load()]), + assert_common_checks(Txn), + + # ... boiler plate is done... + # What is the offset into the vaa of the actual payload? + off.store(Btoi(Extract(Txn.application_args[1], Int(5), Int(1))) * Int(66) + Int(57)), + + # Lets extract it and log it... + MagicAssert(Len(Txn.application_args[1]) > off.load()), + + Log(Extract(Txn.application_args[1], off.load(), Len(Txn.application_args[1]) - off.load())), + + # It is the way... + Approve() + ]) + + # You could wrap your governance in a vaa from a trusted + # governance emitter. For the purposes of this demo, we are + # skipping that. Again, you could look at the core contract or + # the portal contract in wormhole to see examples of doing + # governance with vaa's. + + def registerEmitter(): + return Seq([ + + # The chain comes in as 8 bytes, we will take the last two bytes, append the emitter to it, and set it to zero + App.globalPut(Concat(Txn.application_args[2], Txn.application_args[1]), Int(0)), + + # It is the way... + Approve() + ]) + + METHOD = Txn.application_args[0] + + router = Cond( + [METHOD == Bytes("registerEmitter"), registerEmitter()], + [METHOD == Bytes("receiveMessage"), receiveMessage()], + [METHOD == Bytes("sendMessage"), sendMessage()], + ) + + on_create = Seq( [ + App.globalPut(Bytes("coreid"), Btoi(Txn.application_args[0])), + Return(Int(1)) + ]) + + on_update = Seq( [ + Return(Int(0)) + ] ) + + on_delete = Seq( [ + Return(Int(0)) + ] ) + + on_optin = Seq( [ + Return(Int(1)) + ] ) + + return Cond( + [Txn.application_id() == Int(0), on_create], + [Txn.on_completion() == OnComplete.UpdateApplication, on_update], + [Txn.on_completion() == OnComplete.DeleteApplication, on_delete], + [Txn.on_completion() == OnComplete.OptIn, on_optin], + [Txn.on_completion() == OnComplete.NoOp, router] + ) + +def get_test_app(client: AlgodClient) -> Tuple[bytes, bytes]: + APPROVAL_PROGRAM = fullyCompileContract(client, approve_app()) + CLEAR_STATE_PROGRAM = fullyCompileContract(client, clear_app()) + + return APPROVAL_PROGRAM, CLEAR_STATE_PROGRAM + +def waitForTransaction( + client: AlgodClient, txID: str, timeout: int = 10 +) -> PendingTxnResponse: + lastStatus = client.status() + lastRound = lastStatus["last-round"] + startRound = lastRound + + while lastRound < startRound + timeout: + pending_txn = client.pending_transaction_info(txID) + + if pending_txn.get("confirmed-round", 0) > 0: + return PendingTxnResponse(pending_txn) + + if pending_txn["pool-error"]: + raise Exception("Pool error: {}".format(pending_txn["pool-error"])) + + lastStatus = client.status_after_block(lastRound + 1) + + lastRound += 1 + + raise Exception( + "Transaction {} not confirmed after {} rounds".format(txID, timeout) + ) + + +if __name__ == "__main__": + #algod_address = "https://testnet-api.algonode.cloud" + #algod_address = "https://mainnet-api.algonode.cloud" + algod_address = sys.argv[3] + algod_token="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + client = AlgodClient(algod_token, algod_address) + + approval, clear = get_test_app(client) + + globalSchema = transaction.StateSchema(num_uints=2, num_byte_slices=40) + # localSchema is IMPORTANT.. you need 16 byte slices + localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=16) + + sender = Account.FromMnemonic(sys.argv[2]) + + coreid = int(sys.argv[1]) + + txn = transaction.ApplicationCreateTxn( + sender=sender.getAddress(), + on_complete=transaction.OnComplete.NoOpOC, + approval_program=b64decode(approval["result"]), + clear_program=b64decode(clear["result"]), + global_schema=globalSchema, + local_schema=localSchema, + sp=client.suggested_params(), + app_args = [coreid] + ) + + signedTxn = txn.sign(sender.getPrivateKey()) + + print("creating app") + client.send_transaction(signedTxn) + + response = waitForTransaction(client, signedTxn.get_txid()) + + messenger=response.applicationIndex + + #print("done.. Handing it some money") + txn = transaction.PaymentTxn(sender = sender.getAddress(), sp = client.suggested_params(), receiver = get_application_address(response.applicationIndex), amt = 100000) + signedTxn = txn.sign(sender.getPrivateKey()) + client.send_transaction(signedTxn) + + #pprint.pprint({"testapp": str(testapp), "address": get_application_address(testapp), "emitterAddress": decode_address(get_application_address(testapp)).hex()}) + print("App ID:", messenger) + print("Address: ", get_application_address(messenger)) diff --git a/projects/messenger/chains/algorand/package.json b/projects/messenger/chains/algorand/package.json new file mode 100644 index 0000000..ba3942e --- /dev/null +++ b/projects/messenger/chains/algorand/package.json @@ -0,0 +1,12 @@ +{ + "name": "algorand", + "version": "1.0.0", + "description": "", + "main": "index.js", + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "MIT" +} diff --git a/projects/messenger/chains/algorand/requirements.txt b/projects/messenger/chains/algorand/requirements.txt new file mode 100644 index 0000000..4ee2d5d --- /dev/null +++ b/projects/messenger/chains/algorand/requirements.txt @@ -0,0 +1,27 @@ +attrs==21.4.0 +cffi==1.15.0 +colorama==0.4.4 +execnet==1.9.0 +future-fstrings==1.2.0 +iniconfig==1.1.1 +msgpack==1.0.3 +packaging==21.3 +pluggy==1.0.0 +py==1.11.0 +pycparser==2.21 +pycryptodomex==3.12.0 +pydantic==1.9.0 +PyNaCl==1.5.0 +pyparsing==3.0.6 +pyteal==v0.10.1 +py-algorand-sdk==1.10.0b1 +pytest==6.2.5 +pytest-depends==1.0.1 +pytest-forked==1.4.0 +pytest-xdist==2.5.0 +PyYAML==6.0 +toml==0.10.2 +typing-extensions==4.0.1 +uvarint==1.2.0 +eth_abi==2.1.1 +coincurve==16.0.0 diff --git a/projects/messenger/xdapp.config.json b/projects/messenger/example.xdapp.config.json similarity index 88% rename from projects/messenger/xdapp.config.json rename to projects/messenger/example.xdapp.config.json index 8dc906b..ee97867 100644 --- a/projects/messenger/xdapp.config.json +++ b/projects/messenger/example.xdapp.config.json @@ -19,6 +19,10 @@ "deployedAddress": "", "emittedVAAs": [ ] + }, + "algo": { + "type": "algorand", + "rpc": "" } }, "wormhole": { diff --git a/projects/messenger/messenger.js b/projects/messenger/messenger.js index 0b641b0..e3f4d02 100644 --- a/projects/messenger/messenger.js +++ b/projects/messenger/messenger.js @@ -1,10 +1,22 @@ import { exec } from "child_process"; import fs from "fs"; import { ethers } from 'ethers'; +import algo from "algosdk"; import { + getEmitterAddressAlgorand, getEmitterAddressEth, - parseSequenceFromLogEth + getEmitterAddressSolana, + getEmitterAddressTerra, + parseSequenceFromLogEth, + parseSequenceFromLogAlgorand, + uint8ArrayToHex } from "@certusone/wormhole-sdk"; + +import { + optin, + submitVAAHeader +} from "@certusone/wormhole-sdk/lib/cjs/algorand/Algorand.js"; + import fetch from 'node-fetch'; async function main(){ @@ -34,31 +46,86 @@ async function main(){ } }) ); + } else if (network.type == "algorand"){ + console.log(`Deploying Algorand network: ${process.argv[2]} to ${network.rpc}`); + exec( + `cd chains/algorand && python3 messenger.py ${network.bridgeAddress} '${network.mnemonic}' ${network.rpc}:${network.port}`, + ((err,out,errStr) => { + if(err) { + throw new Error(err); + } + + if(out){ + console.log(out); + network.appId = parseInt(out.split("App ID:")[1].split("Address")[0].trim()); + network.deployedAddress = out.split("Address: ")[1].trim(); + network.emittedVAAs = []; + config.networks[process.argv[2]] = network; + fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 4)); + } + }) + ) + } else { + throw new Error("Invalid Network Type!"); } } else if (process.argv[3] == "register_chain") { if(!network.deployedAddress){ throw new Error("Deploy to this network first!"); } + const targetNetwork = config.networks[process.argv[4]]; + if(!targetNetwork.deployedAddress){ + throw new Error("Target Network not deployed yet!"); + } + + let emitterAddr; + if(targetNetwork.type == "evm"){ + emitterAddr = Buffer.from(getEmitterAddressEth(targetNetwork.deployedAddress), "hex"); + } else if (targetNetwork.type == "algorand") { + emitterAddr = Buffer.from(getEmitterAddressAlgorand(targetNetwork.appId), "hex"); + } else if (targetNetwork.type == "solana") { + emitterAddr = Buffer.from(await getEmitterAddressSolana(targetNetwork.deployedAddress), "hex"); + } else if (targetNetwork.type == "terra") { + emitterAddr = Buffer.from(await getEmitterAddressTerra(targetNetwork.deployedAddress), "hex"); + } + + if(network.type == "evm"){ const signer = new ethers.Wallet(network.privateKey) .connect(new ethers.providers.JsonRpcProvider(network.rpc)); - - const targetNetwork = config.networks[process.argv[4]]; - if(!targetNetwork.deployedAddress){ - throw new Error("Target Network not deployed yet!"); - } - if(targetNetwork.type == "evm"){ - const emitter_address = Buffer.from(getEmitterAddressEth(targetNetwork.deployedAddress), "hex"); - const messenger = new ethers.Contract( - network.deployedAddress, - JSON.parse(fs.readFileSync('./chains/evm/out/Messenger.sol/Messenger.json').toString()).abi, - signer - ); - const tx = await messenger.registerApplicationContracts(targetNetwork.wormholeChainId, emitter_address); - console.log(`Network(${process.argv[2]}) Registered EVM style Emitter: `, tx.hash); - } + + const messenger = new ethers.Contract( + network.deployedAddress, + JSON.parse(fs.readFileSync('./chains/evm/out/Messenger.sol/Messenger.json').toString()).abi, + signer + ); + await messenger.registerApplicationContracts(targetNetwork.wormholeChainId, emitterAddr); + } else if (network.type == "algorand"){ + const algodClient = new algo.Algodv2( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + network.rpc, + network.port + ); + const sender = algo.mnemonicToSecretKey(network.mnemonic); + const params = await algodClient.getTransactionParams().do(); + let txs = []; + txs.push({ + tx: algo.makeApplicationCallTxnFromObject({ + appArgs: [ + Uint8Array.from(Buffer.from("registerEmitter")), + Uint8Array.from(emitterAddr), + algo.bigIntToBytes(BigInt(targetNetwork.wormholeChainId), 2) + ], + appIndex: network.appId, + from: sender.addr, + onComplete: algo.OnApplicationComplete.NoOpOC, + suggestedParams: params, + }), + signer: null, + }); + await signSendAndConfirmAlgorand(algodClient, txs, sender); } + console.log(`Network(${process.argv[2]}) Registered Emitter: ${targetNetwork.deployedAddress} from Chain: ${targetNetwork.wormholeChainId}`); } else if (process.argv[3] == "send_msg") { if(!network.deployedAddress){ throw new Error("Deploy to this network first!"); @@ -92,17 +159,72 @@ async function main(){ config.networks[process.argv[2]] = network; fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 2)); console.log(`Network(${process.argv[2]}) Emitted VAA: `, vaaBytes.vaaBytes); + } else if (network.type == "algorand"){ + const algodClient = new algo.Algodv2( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + network.rpc, + network.port + ); + const sender = algo.mnemonicToSecretKey(network.mnemonic); + const params = await algodClient.getTransactionParams().do(); + let txs = []; + + //Opt In to allow core brige to store data with Algo Contract + const messengerPubkey = uint8ArrayToHex(algo.decodeAddress(network.deployedAddress).publicKey); + const { addr: emitterAddr, txs: emitterOptInTxs } = await optin( + algodClient, + sender.addr, + BigInt(network.bridgeAddress), + BigInt(0), + messengerPubkey + ); + txs.push(...emitterOptInTxs); + let accts = [ + emitterAddr, + algo.getApplicationAddress(network.bridgeAddress), + ]; + let appTxn = algo.makeApplicationCallTxnFromObject({ + appArgs: [ + Uint8Array.from(Buffer.from("sendMessage")), + Uint8Array.from(Buffer.from(process.argv[4])) + ], + accounts: accts, + appIndex: network.appId, + foreignApps: [network.bridgeAddress], + from: sender.addr, + onComplete: algo.OnApplicationComplete.NoOpOC, + suggestedParams: params, + }); + appTxn.fee *= 2; + txs.push({tx: appTxn, signer: null}); + const receipt = await signSendAndConfirmAlgorand(algodClient, txs, sender); + const emitAddr = getEmitterAddressAlgorand(network.appId); + const seq = parseSequenceFromLogAlgorand(receipt); + await new Promise((r) => setTimeout(r, 10000)); + const vaaBytes = await ( + await fetch( + `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitAddr}/${seq}` + ) + ).json(); + if(!network.emittedVAAs){ + network.emittedVAAs = [vaaBytes.vaaBytes]; + } else { + network.emittedVAAs.push(vaaBytes.vaaBytes); + } + config.networks[process.argv[2]] = network; + fs.writeFileSync('./xdapp.config.json', JSON.stringify(config, null, 2)); + console.log(`Network(${process.argv[2]}) Emitted VAA: `, vaaBytes.vaaBytes); } } else if (process.argv[3] == "submit_vaa") { if(!network.deployedAddress){ throw new Error("Deploy to this network first!"); } - + const targetNetwork = config.networks[process.argv[4]]; + const vaaBytes = isNaN(parseInt(process.argv[5])) ? + targetNetwork.emittedVAAs.pop() : + targetNetwork.emittedVAAs[parseInt(process.argv[5])]; + if(network.type == "evm"){ - const targetNetwork = config.networks[process.argv[4]]; - const vaaBytes = isNaN(parseInt(process.argv[5])) ? - targetNetwork.emittedVAAs.pop() : - targetNetwork.emittedVAAs[parseInt(process.argv[5])]; const signer = new ethers.Wallet(network.privateKey) .connect(new ethers.providers.JsonRpcProvider(network.rpc)); @@ -114,6 +236,37 @@ async function main(){ const tx = await messenger.receiveEncodedMsg(Buffer.from(vaaBytes, "base64")); console.log(`Submitted VAA: ${vaaBytes}\nTX: ${tx.hash}`); + } else if (network.type == "algorand"){ + const algodClient = new algo.Algodv2( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + network.rpc, + network.port + ); + const sender = algo.mnemonicToSecretKey(network.mnemonic); + const params = await algodClient.getTransactionParams().do(); + let txs = []; + + let sstate = await submitVAAHeader(algodClient, BigInt(network.bridgeAddress), Uint8Array.from(Buffer.from(vaaBytes, "base64")), sender.addr, BigInt(network.appId)) + txs = sstate.txs; + let accts = sstate.accounts; + + txs.push({ + tx: algo.makeApplicationCallTxnFromObject({ + appArgs: [ + Uint8Array.from(Buffer.from(("receiveMessage"))), + Uint8Array.from(Buffer.from(vaaBytes, "base64")) + ], + accounts: accts, + appIndex: network.appId, + from: sender.addr, + onComplete: algo.OnApplicationComplete.NoOpOC, + suggestedParams: params, + }), + signer: null, + }); + + ret = await signSendAndConfirmAlgorand(algodClient, txs, sender); + console.log(ret); } } else if (process.argv[3] == "get_current_msg") { if(!network.deployedAddress){ @@ -134,4 +287,27 @@ async function main(){ } } +async function signSendAndConfirmAlgorand( + algodClient, + txs, + wallet +) { + algo.assignGroupID(txs.map((tx) => tx.tx)); + const signedTxns = []; + for (const tx of txs) { + if (tx.signer) { + signedTxns.push(await tx.signer.signTxn(tx.tx)); + } else { + signedTxns.push(tx.tx.signTxn(wallet.sk)); + } + } + await algodClient.sendRawTransaction(signedTxns).do(); + const result = await algo.waitForConfirmation( + algodClient, + txs[txs.length - 1].tx.txID(), + 1 + ); + return result; +} + main(); \ No newline at end of file diff --git a/projects/messenger/package-lock.json b/projects/messenger/package-lock.json index 3727e30..23792f6 100644 --- a/projects/messenger/package-lock.json +++ b/projects/messenger/package-lock.json @@ -9,14 +9,22 @@ "version": "1.0.0", "license": "MIT", "workspaces": [ - "chains/evm" + "chains/evm", + "chains/algorand" ], "dependencies": { "@certusone/wormhole-sdk": "^0.3.3", + "algosdk": "^1.16.0", + "byteify": "^2.0.10", "ethers": "^5.6.6", "node-fetch": "^2.6.7" } }, + "chains/algorand": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": {} + }, "node_modules/@babel/runtime": { "version": "7.17.9", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz", @@ -1014,6 +1022,10 @@ "node": ">= 10" } }, + "node_modules/algorand": { + "resolved": "chains/algorand", + "link": true + }, "node_modules/algosdk": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.16.0.tgz", @@ -1242,6 +1254,11 @@ "node": ">=6.14.2" } }, + "node_modules/byteify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/byteify/-/byteify-2.0.10.tgz", + "integrity": "sha512-clrE0NtRB/YwjQcmrUU9qpxRIQ5Jc1HGedv6/LWd08upE0FV0S4YvPBkmKEsTaquqGmhx34LkRBO+lXpTgwYgw==" + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -2995,6 +3012,9 @@ "resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz", "integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==" }, + "algorand": { + "version": "file:chains/algorand" + }, "algosdk": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.16.0.tgz", @@ -3182,6 +3202,11 @@ "node-gyp-build": "^4.3.0" } }, + "byteify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/byteify/-/byteify-2.0.10.tgz", + "integrity": "sha512-clrE0NtRB/YwjQcmrUU9qpxRIQ5Jc1HGedv6/LWd08upE0FV0S4YvPBkmKEsTaquqGmhx34LkRBO+lXpTgwYgw==" + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", diff --git a/projects/messenger/package.json b/projects/messenger/package.json index 7abb5a1..2f9d56c 100644 --- a/projects/messenger/package.json +++ b/projects/messenger/package.json @@ -10,11 +10,14 @@ "author": "", "license": "MIT", "workspaces": [ - "chains/evm" + "chains/evm", + "chains/algorand" ], "type": "module", "dependencies": { "@certusone/wormhole-sdk": "^0.3.3", + "algosdk": "^1.16.0", + "byteify": "^2.0.10", "ethers": "^5.6.6", "node-fetch": "^2.6.7" } diff --git a/projects/messenger/tests/eth0-eth1.sh b/projects/messenger/tests/eth0-eth1.sh index d51eb27..7df65bd 100644 --- a/projects/messenger/tests/eth0-eth1.sh +++ b/projects/messenger/tests/eth0-eth1.sh @@ -1,4 +1,3 @@ -cd ../ node messenger.js eth0 deploy node messenger.js eth1 deploy node messenger.js eth0 register_chain eth1