From a48b0e1a7b2d8eef118a177acb56384412d97c34 Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Thu, 16 Nov 2023 15:03:18 -0600 Subject: [PATCH 1/6] add support for running cadence from 1.1.0 flix json files --- .../derive-cadence-by-network.js | 41 ++- .../derive-cadence-by-network.test.js | 258 +++++++++++++++++- .../interaction-template.js | 3 +- 3 files changed, 298 insertions(+), 4 deletions(-) diff --git a/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js b/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js index ba6376936..d7d74045b 100644 --- a/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js +++ b/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js @@ -62,13 +62,50 @@ export function deriveCadenceByNetwork({network, template}) { return [dependencyPlaceholder, dependencyContractForNetwork.address] } - ) - + ) + return networkDependencies.reduce((cadence, [placeholder, address]) => { const regex = new RegExp("(\\b" + placeholder + "\\b)", "g") return cadence.replace(regex, address) }, template.data.cadence) + case "1.1.0": + // get network dependencies from template dependencies, use new string import format + const networkDeps = {} + + template?.data?.dependencies.forEach(dependency => { + dependency.contracts.forEach(contract => { + const contractName = contract.contract + const networkAddress = null + contract.networks.forEach(net => { + if (net.network === network) { + networkDeps[contractName] = net.address + } + }) + + invariant( + networkAddress, + `networkAddress -- Could not find contracts Network Address: ${contractName}` + ) + }) + }) + + invariant( + Object.keys(networkDeps).length === template?.data?.dependencies.length, + `networkDeps -- Could not find contracts for import dependencies: ${networkDeps}` + ) + + invariant( + Object.keys(networkDeps).length === Object.values(networkDeps).length, + `networkDeps -- Could not find all addresses for network ${network} dependencies: ${networkDeps}` + ) + + console.log("networkDeps", networkDeps) + return Object.keys(networkDeps).reduce((cadence, contractName) => { + const test = new RegExp(`\\bimport\\b\\s*\\\"${contractName}\\\"`, "g") + return cadence.replace(test, `import ${contractName} from ${networkDeps[contractName]}`) + }, template.data.cadence.body) + default: throw new Error( "deriveCadenceByNetwork Error: Unsupported template version" diff --git a/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.test.js b/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.test.js index b21c67b6d..518012ac9 100644 --- a/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.test.js +++ b/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.test.js @@ -1,6 +1,6 @@ import {deriveCadenceByNetwork} from "./derive-cadence-by-network.js" -describe("Derive cadence by network", () => { +describe("Derive cadence by network 1.0.0", () => { let template = { f_type: "InteractionTemplate", f_version: "1.0.0", @@ -52,3 +52,259 @@ describe("Derive cadence by network", () => { ).toThrow(Error) }) }) + +describe("Derive cadence by network 1.1.0 single import", () => { + let templatev11 = { + f_type: "InteractionTemplate", + f_version: "1.1.0", + id: "a2b2d73def...aabc5472d2", + data: { + type: "transaction", + interface: "asadf23234...fas234234", + messages: [], + cadence: { + body: "import \"FlowToken\"\n transaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n prepare(signer: AuthAccount) {\n %%self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n self.vault <- FungibleToken.getVault(signer)\n }\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n }", + pins: [ + { + network: "mainnet", + pin: "186e262ce6fe06b5075ec6569a0e5482a79c471881182612d8e4a665c2977f3e", + }, + { + network: "testnet", + pin: "f93977d7a297f559e97259cb2a95fed0f87cfeec46c5257a26adc26a260d6c4c", + }, + ], + }, + dependencies: [ + { + contracts: [ + { + contract: "FlowToken", + networks: [ + { + network: "mainnet", + address: "0x1654653399040a61", + dependency_pin_block_height: 10123123123, + dependency_pin: { + pin: "c8cb7cc7a1c2a329de65d83455016bc3a9b53f9668c74ef555032804bac0b25b", + pin_self: + "38d0cca4b74c4e88213df636b4cfc2eb6e86fd8b2b84579d3b9bffab3e0b1fcb", + pin_contract_name: "FlowToken", + imports: [ + { + pin: "b8a3ed26c222ed67016a28021d8fee5603b948533cbc992b3c90f71a61b2b312", + pin_self: + "7bc3056ba5d39d130f45411c2c05bb549db8ce727c11a1cb821136a621be27fb", + pin_contract_name: "FungibleToken", + pin_contract_address: "0xf233dcee88fe0abe", + imports: [], + }, + ], + }, + }, + { + network: "testnet", + address: "0x7e60df042a9c0868", + dependency_pin_block_height: 10123123123, + dependency_pin: { + pin: "c8cb7cc7a1c2a329de65d83455016bc3a9b53f9668c74ef555032804bac0b25b", + pin_self: + "38d0cca4b74c4e88213df636b4cfc2eb6e86fd8b2b84579d3b9bffab3e0b1fcb", + pin_contract_name: "FlowToken", + pin_contract_address: "0x7e60df042a9c0868", + imports: [], + }, + }, + ], + }, + ], + }, + ], + parameters: [ + { + label: "amount", + index: 0, + type: "UFix64", + messages: [], + balance: "FlowToken", + }, + { + label: "to", + index: 1, + type: "Address", + messages: [], + }, + ], + }, + } + + const mainnetAddressReplaced = "import FlowToken from 0x1654653399040a61\n transaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n prepare(signer: AuthAccount) {\n %%self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n self.vault <- FungibleToken.getVault(signer)\n }\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n }" + test("1.1.0 derives cadence correctly for a given mainnet", async () => { + let cadence = deriveCadenceByNetwork({ + network: "mainnet", + template: templatev11, + }) + + expect(cadence).toEqual(mainnetAddressReplaced) + + + }) + + const testnetAddressReplaced = "import FlowToken from 0x7e60df042a9c0868\n transaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n prepare(signer: AuthAccount) {\n %%self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n self.vault <- FungibleToken.getVault(signer)\n }\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n }" + test("1.1.0 derives cadence correctly for a given testnet", async () => { + let cadence = deriveCadenceByNetwork({ + network: "testnet", + template: templatev11, + }) + + expect(cadence).toEqual(testnetAddressReplaced) + }) + + test("It fails to derive cadence for unknown network", async () => { + expect(() => + deriveCadenceByNetwork({ + network: "randomnet", + templatev11, + }) + ).toThrow(Error) + }) +}) + + +describe("Derive cadence by network 1.1.0 multiple import", () => { + let templatev11 = { + f_type: "InteractionTemplate", + f_version: "1.1.0", + id: "a2b2d73def...aabc5472d2", + data: { + type: "transaction", + interface: "asadf23234...fas234234", + messages: [], + cadence: { + body: "import \"FlowToken\"\n import \"FungibleToken\"\n transaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n prepare(signer: AuthAccount) {\n %%self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n self.vault <- FungibleToken.getVault(signer)\n }\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n }", + pins: [ + { + network: "mainnet", + pin: "186e262ce6fe06b5075ec6569a0e5482a79c471881182612d8e4a665c2977f3e", + }, + { + network: "testnet", + pin: "f93977d7a297f559e97259cb2a95fed0f87cfeec46c5257a26adc26a260d6c4c", + }, + ], + }, + dependencies: [ + { + contracts: [ + { + contract: "FlowToken", + networks: [ + { + network: "mainnet", + address: "0x1654653399040a61", + dependency_pin_block_height: 10123123123, + dependency_pin: { + pin: "c8cb7cc7a1c2a329de65d83455016bc3a9b53f9668c74ef555032804bac0b25b", + pin_self: + "38d0cca4b74c4e88213df636b4cfc2eb6e86fd8b2b84579d3b9bffab3e0b1fcb", + pin_contract_name: "FlowToken", + imports: [], + }, + }, + { + network: "testnet", + address: "0x7e60df042a9c0868", + dependency_pin_block_height: 10123123123, + dependency_pin: { + pin: "c8cb7cc7a1c2a329de65d83455016bc3a9b53f9668c74ef555032804bac0b25b", + pin_self: + "38d0cca4b74c4e88213df636b4cfc2eb6e86fd8b2b84579d3b9bffab3e0b1fcb", + pin_contract_name: "FlowToken", + pin_contract_address: "0x7e60df042a9c0868", + imports: [], + }, + }, + ], + }, + { + contract: "FungibleToken", + networks: [ + { + network: "mainnet", + address: "0xf233dcee88fe0abe", + dependency_pin_block_height: 10123123123, + dependency_pin: { + pin: "c8cb7cc7a1c2a329de65d83455016bc3a9b53f9668c74ef555032804bac0b25b", + pin_self: + "38d0cca4b74c4e88213df636b4cfc2eb6e86fd8b2b84579d3b9bffab3e0b1fcb", + pin_contract_name: "FlowToken", + imports: [], + }, + }, + { + network: "testnet", + address: "0x11111111111", + dependency_pin_block_height: 10123123123, + dependency_pin: { + pin: "c8cb7cc7a1c2a329de65d83455016bc3a9b53f9668c74ef555032804bac0b25b", + pin_self: + "38d0cca4b74c4e88213df636b4cfc2eb6e86fd8b2b84579d3b9bffab3e0b1fcb", + pin_contract_name: "FlowToken", + pin_contract_address: "0x7e60df042a9c0868", + imports: [], + }, + }, + ], + }, + ], + }, + ], + parameters: [ + { + label: "amount", + index: 0, + type: "UFix64", + messages: [], + balance: "FlowToken", + }, + { + label: "to", + index: 1, + type: "Address", + messages: [], + }, + ], + }, + } + + const mainnetAddressReplaced = "import FlowToken from 0x1654653399040a61\n import FungibleToken from 0xf233dcee88fe0abe\n transaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n prepare(signer: AuthAccount) {\n %%self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n self.vault <- FungibleToken.getVault(signer)\n }\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n }" + test("1.1.0 multiple imports derives cadence correctly for a given mainnet", async () => { + let cadence = deriveCadenceByNetwork({ + network: "mainnet", + template: templatev11, + }) + + expect(cadence).toEqual(mainnetAddressReplaced) + + + }) + + const testnetAddressReplaced = "import FlowToken from 0x7e60df042a9c0868\n import FungibleToken from 0x11111111111\n transaction(amount: UFix64, to: Address) {\n let vault: @FungibleToken.Vault\n prepare(signer: AuthAccount) {\n %%self.vault <- signer\n .borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!\n .withdraw(amount: amount)\n self.vault <- FungibleToken.getVault(signer)\n }\n execute {\n getAccount(to)\n .getCapability(/public/flowTokenReceiver)!\n .borrow<&{FungibleToken.Receiver}>()!\n .deposit(from: <-self.vault)\n }\n }" + test("1.1.0 multiple imports derives cadence correctly for a given testnet", async () => { + let cadence = deriveCadenceByNetwork({ + network: "testnet", + template: templatev11, + }) + + expect(cadence).toEqual(testnetAddressReplaced) + }) + + test("1.1.0 multiple imports fails to derive cadence for unknown network", async () => { + expect(() => + deriveCadenceByNetwork({ + network: "randomnet", + templatev11, + }) + ).toThrow(Error) + }) +}) \ No newline at end of file diff --git a/packages/fcl/src/normalizers/interaction-template/interaction-template.js b/packages/fcl/src/normalizers/interaction-template/interaction-template.js index 459906df1..3b32bd912 100644 --- a/packages/fcl/src/normalizers/interaction-template/interaction-template.js +++ b/packages/fcl/src/normalizers/interaction-template/interaction-template.js @@ -4,7 +4,8 @@ export function normalizeInteractionTemplate(template) { switch (template["f_version"]) { case "1.0.0": return template - + case "1.1.0": + return template default: throw new Error( "normalizeInteractionTemplate Error: Invalid InteractionTemplate" From 5c9465b9e8ccc0a2e142a951f9ef7fef6a39cabe Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Thu, 16 Nov 2023 15:16:36 -0600 Subject: [PATCH 2/6] add changeset for beta release support v1.1.0 --- .changeset/slimy-buses-confess.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/slimy-buses-confess.md diff --git a/.changeset/slimy-buses-confess.md b/.changeset/slimy-buses-confess.md new file mode 100644 index 000000000..ea09c3b1e --- /dev/null +++ b/.changeset/slimy-buses-confess.md @@ -0,0 +1,5 @@ +--- +"@onflow/fcl": patch +--- + +Add initial support to execute FLIX 1.1.0 for beta testing From 49fc154f767685a25245de8f10a65fa0c7576af9 Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Thu, 16 Nov 2023 16:04:03 -0600 Subject: [PATCH 3/6] update other module with network replace algo and clean up --- .../fcl/src/exec/utils/derive-dependencies.js | 20 +++++++++++++++++++ .../derive-cadence-by-network.js | 6 ++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/fcl/src/exec/utils/derive-dependencies.js b/packages/fcl/src/exec/utils/derive-dependencies.js index 20f122e83..afb08af65 100644 --- a/packages/fcl/src/exec/utils/derive-dependencies.js +++ b/packages/fcl/src/exec/utils/derive-dependencies.js @@ -44,6 +44,26 @@ export async function deriveDependencies(opts = {}) { return derivedDependencies + case "1.1.0": + let derivedDependencies = {} + template?.data?.dependencies?.forEach(dependency => { + dependency.contracts.forEach(contract => { + const contractName = contract.contract + contract.networks.forEach(net => { + if (net.network === network) { + derivedDependencies[contractName] = net.address + } + }) + + invariant( + derivedDependencies[contractName], + `networkAddress -- Could not find contracts Network Address: ${network} ${contractName}` + ) + }) + }) + + return derivedDependencies + default: throw new Error( "FCL configureDependencies Error: Unsupported template version" diff --git a/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js b/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js index d7d74045b..a20be3852 100644 --- a/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js +++ b/packages/fcl/src/interaction-template-utils/derive-cadence-by-network.js @@ -76,7 +76,6 @@ export function deriveCadenceByNetwork({network, template}) { template?.data?.dependencies.forEach(dependency => { dependency.contracts.forEach(contract => { const contractName = contract.contract - const networkAddress = null contract.networks.forEach(net => { if (net.network === network) { networkDeps[contractName] = net.address @@ -84,8 +83,8 @@ export function deriveCadenceByNetwork({network, template}) { }) invariant( - networkAddress, - `networkAddress -- Could not find contracts Network Address: ${contractName}` + networkDeps[contractName], + `networkAddress -- Could not find contracts Network Address: ${network} ${contractName}` ) }) }) @@ -100,7 +99,6 @@ export function deriveCadenceByNetwork({network, template}) { `networkDeps -- Could not find all addresses for network ${network} dependencies: ${networkDeps}` ) - console.log("networkDeps", networkDeps) return Object.keys(networkDeps).reduce((cadence, contractName) => { const test = new RegExp(`\\bimport\\b\\s*\\\"${contractName}\\\"`, "g") return cadence.replace(test, `import ${contractName} from ${networkDeps[contractName]}`) From 98acf368d1ce2991a17a6fe9bc91a3c3290b1db3 Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Thu, 16 Nov 2023 16:16:28 -0600 Subject: [PATCH 4/6] make sure address is prefixed --- packages/fcl/src/exec/utils/derive-dependencies.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/fcl/src/exec/utils/derive-dependencies.js b/packages/fcl/src/exec/utils/derive-dependencies.js index afb08af65..de8576166 100644 --- a/packages/fcl/src/exec/utils/derive-dependencies.js +++ b/packages/fcl/src/exec/utils/derive-dependencies.js @@ -45,13 +45,12 @@ export async function deriveDependencies(opts = {}) { return derivedDependencies case "1.1.0": - let derivedDependencies = {} template?.data?.dependencies?.forEach(dependency => { dependency.contracts.forEach(contract => { const contractName = contract.contract contract.networks.forEach(net => { if (net.network === network) { - derivedDependencies[contractName] = net.address + derivedDependencies[contractName] = withPrefix(net?.address) } }) From c4b8b88dc7ddebd2da8e664a9eb98cb426ef2b0a Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Thu, 16 Nov 2023 16:52:26 -0600 Subject: [PATCH 5/6] update to be a minor release --- .changeset/healthy-crabs-unite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/healthy-crabs-unite.md diff --git a/.changeset/healthy-crabs-unite.md b/.changeset/healthy-crabs-unite.md new file mode 100644 index 000000000..1c12d08b3 --- /dev/null +++ b/.changeset/healthy-crabs-unite.md @@ -0,0 +1,5 @@ +--- +"@onflow/fcl": minor +--- + +Add support for running FLIX v1.1.0 (verified interactions) From 271b10f47542f74d9d151e1f21fbe16e41ab74cd Mon Sep 17 00:00:00 2001 From: Tom Haile Date: Thu, 16 Nov 2023 16:55:01 -0600 Subject: [PATCH 6/6] remove patch changeset, going to do minor --- .changeset/slimy-buses-confess.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/slimy-buses-confess.md diff --git a/.changeset/slimy-buses-confess.md b/.changeset/slimy-buses-confess.md deleted file mode 100644 index ea09c3b1e..000000000 --- a/.changeset/slimy-buses-confess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@onflow/fcl": patch ---- - -Add initial support to execute FLIX 1.1.0 for beta testing