From c9f492fd53192aa03614556ed6c99828c5f4d6b7 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:22:17 -0600 Subject: [PATCH 1/6] fix NFT templates --- ...ridged-nft-code-chunks-args-crescendo.json | 5 +- ...bridged-nft-code-chunks-args-emulator.json | 5 +- ...idged-nft-code-chunks-args-previewnet.json | 5 +- .../emulator/EVMBridgedNFTTemplate.cdc | 5 +- .../previewnet/EVMBridgedNFTTemplate.cdc | 5 +- .../testnet/EVMBridgedNFTTemplate.cdc | 380 ++++++++++++++++++ .../testnet/EVMBridgedTokenTemplate.cdc | 271 +++++++++++++ 7 files changed, 656 insertions(+), 20 deletions(-) create mode 100644 cadence/contracts/templates/testnet/EVMBridgedNFTTemplate.cdc create mode 100644 cadence/contracts/templates/testnet/EVMBridgedTokenTemplate.cdc diff --git a/cadence/args/bridged-nft-code-chunks-args-crescendo.json b/cadence/args/bridged-nft-code-chunks-args-crescendo.json index 9d6aa699..635328fd 100644 --- a/cadence/args/bridged-nft-code-chunks-args-crescendo.json +++ b/cadence/args/bridged-nft-code-chunks-args-crescendo.json @@ -55,10 +55,7 @@ "value": "2e4e46540a0a2020202020202020202020202f2f2061646420746865206e657720746f6b656e20746f207468652064696374696f6e6172792077686963682072656d6f76657320746865206f6c64206f6e650a20202020202020202020202073656c662e65766d4944546f466c6f7749445b746f6b656e2e65766d49445d203d20746f6b656e2e69640a2020202020202020202020206c6574206f6c64546f6b656e203c2d2073656c662e6f776e65644e4654735b746f6b656e2e69645d203c2d20746f6b656e0a0a20202020202020202020202064657374726f79206f6c64546f6b656e0a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320616e206172726179206f66207468652049447320746861742061726520696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e2067657449447328293a205b55496e7436345d207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320616e206172726179206f66207468652045564d2049447320746861742061726520696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e2067657445564d49447328293a205b55496e743235365d207b0a20202020202020202020202072657475726e2073656c662e65766d4944546f466c6f7749442e6b6579730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074686520436164656e6365204e46542e696420666f722074686520676976656e2045564d204e46542049442069660a202020202020202061636365737328616c6c2920766965772066756e20676574436164656e636549442866726f6d2065766d49443a2055496e74323536293a2055496e7436343f207b0a20202020202020202020202072657475726e2073656c662e65766d4944546f466c6f7749445b65766d49445d203f3f2055496e7436342865766d4944290a20202020202020207d0a0a20202020202020202f2f2f2052657475726e73207468652045564d204e4654204944206173736f63696174656420776974682074686520436164656e6365204e46542049442e2054686520676f616c20697320746f20726574726965766520746865204552433732312049442076616c75652e0a20202020202020202f2f2f20417320666172206173207468652062726964676520697320636f6e6365726e65642c20616e2045524337323120646566696e6564206279207468652062726964676520697320746865204e46542773204944206174207468652074696d65206f66206272696467696e670a20202020202020202f2f2f206f72207468652076616c7565206f6620746865204e46542e65766d494420696620697420696d706c656d656e7473207468652043726f7373564d4e46542e45564d4e465420696e74657266616365207768656e20627269646765642e0a20202020202020202f2f2f20466f6c6c6f77696e672074686973207061747465726e2c206966206c6f636b65642c20746865204e465420697320636865636b656420666f722045564d4e465420636f6e666f726d616e63652072657475726e696e67202e65766d494420696620736f2c0a20202020202020202f2f2f206f746865727769736520746865204e465427732049442069732072657475726e656420617320612055496e743235362073696e63652074686174277320686f77207468652062726964676520776f756c642068616e646c65206d696e74696e6720696e207468650a20202020202020202f2f2f20636f72726573706f6e64696e672045524337323120636f6e74726163742e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e2067657445564d49442866726f6d20636164656e636549443a2055496e743634293a2055496e743235363f207b0a2020202020202020202020206966206c6574206e6674203d2073656c662e626f72726f774e465428636164656e6365494429207b0a202020202020202020202020202020206966206c65742065766d4e4654203d2043726f7373564d4e46542e67657445564d49442866726f6d3a206e667429207b0a202020202020202020202020202020202020202072657475726e2065766d4e46540a202020202020202020202020202020207d0a2020202020202020202020202020202072657475726e2055496e74323536286e66742e6964290a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074686520636f6e747261637455524920666f7220746865204e465420636f6c6c656374696f6e20617320646566696e656420696e2074686520736f757263652045524337323120636f6e74726163742e204966206e6f6e65207761730a20202020202020202f2f2f20646566696e6564206174207468652074696d65206f66206272696467696e672c20616e20656d70747920737472696e672069732072657475726e65642e0a202020202020202061636365737328616c6c2920766965772066756e20636f6e747261637455524928293a20537472696e673f207b0a20202020202020202020202072657475726e20" }, { "type": "String", - "value": "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a2020202020202020202020206966206c6574206e6674203d202673656c662e6f776e65644e4654735b69645d2061732026" - }, { - "type": "String", - "value": "2e4e46543f207b0a2020202020202020202020202020202072657475726e206e667420617320267b566965775265736f6c7665722e5265736f6c7665727d0a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d" + "value": "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d20617320267b566965775265736f6c7665722e5265736f6c7665727d3f203f3f206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d" }, { "type": "String", "value": "2e637265617465456d707479436f6c6c656374696f6e286e6674547970653a20547970653c40" diff --git a/cadence/args/bridged-nft-code-chunks-args-emulator.json b/cadence/args/bridged-nft-code-chunks-args-emulator.json index b83f8971..8df73a5c 100644 --- a/cadence/args/bridged-nft-code-chunks-args-emulator.json +++ b/cadence/args/bridged-nft-code-chunks-args-emulator.json @@ -55,10 +55,7 @@ "value": "2e4e46540a0a2020202020202020202020202f2f2061646420746865206e657720746f6b656e20746f207468652064696374696f6e6172792077686963682072656d6f76657320746865206f6c64206f6e650a20202020202020202020202073656c662e65766d4944546f466c6f7749445b746f6b656e2e65766d49445d203d20746f6b656e2e69640a2020202020202020202020206c6574206f6c64546f6b656e203c2d2073656c662e6f776e65644e4654735b746f6b656e2e69645d203c2d20746f6b656e0a0a20202020202020202020202064657374726f79206f6c64546f6b656e0a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320616e206172726179206f66207468652049447320746861742061726520696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e2067657449447328293a205b55496e7436345d207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320616e206172726179206f66207468652045564d2049447320746861742061726520696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e2067657445564d49447328293a205b55496e743235365d207b0a20202020202020202020202072657475726e2073656c662e65766d4944546f466c6f7749442e6b6579730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074686520436164656e6365204e46542e696420666f722074686520676976656e2045564d204e46542049442069660a202020202020202061636365737328616c6c2920766965772066756e20676574436164656e636549442866726f6d2065766d49443a2055496e74323536293a2055496e7436343f207b0a20202020202020202020202072657475726e2073656c662e65766d4944546f466c6f7749445b65766d49445d203f3f2055496e7436342865766d4944290a20202020202020207d0a0a20202020202020202f2f2f2052657475726e73207468652045564d204e4654204944206173736f63696174656420776974682074686520436164656e6365204e46542049442e2054686520676f616c20697320746f20726574726965766520746865204552433732312049442076616c75652e0a20202020202020202f2f2f20417320666172206173207468652062726964676520697320636f6e6365726e65642c20616e2045524337323120646566696e6564206279207468652062726964676520697320746865204e46542773204944206174207468652074696d65206f66206272696467696e670a20202020202020202f2f2f206f72207468652076616c7565206f6620746865204e46542e65766d494420696620697420696d706c656d656e7473207468652043726f7373564d4e46542e45564d4e465420696e74657266616365207768656e20627269646765642e0a20202020202020202f2f2f20466f6c6c6f77696e672074686973207061747465726e2c206966206c6f636b65642c20746865204e465420697320636865636b656420666f722045564d4e465420636f6e666f726d616e63652072657475726e696e67202e65766d494420696620736f2c0a20202020202020202f2f2f206f746865727769736520746865204e465427732049442069732072657475726e656420617320612055496e743235362073696e63652074686174277320686f77207468652062726964676520776f756c642068616e646c65206d696e74696e6720696e207468650a20202020202020202f2f2f20636f72726573706f6e64696e672045524337323120636f6e74726163742e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e2067657445564d49442866726f6d20636164656e636549443a2055496e743634293a2055496e743235363f207b0a2020202020202020202020206966206c6574206e6674203d2073656c662e626f72726f774e465428636164656e6365494429207b0a202020202020202020202020202020206966206c65742065766d4e4654203d2043726f7373564d4e46542e67657445564d49442866726f6d3a206e667429207b0a202020202020202020202020202020202020202072657475726e2065766d4e46540a202020202020202020202020202020207d0a2020202020202020202020202020202072657475726e2055496e74323536286e66742e6964290a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074686520636f6e747261637455524920666f7220746865204e465420636f6c6c656374696f6e20617320646566696e656420696e2074686520736f757263652045524337323120636f6e74726163742e204966206e6f6e65207761730a20202020202020202f2f2f20646566696e6564206174207468652074696d65206f66206272696467696e672c20616e20656d70747920737472696e672069732072657475726e65642e0a202020202020202061636365737328616c6c2920766965772066756e20636f6e747261637455524928293a20537472696e673f207b0a20202020202020202020202072657475726e20" }, { "type": "String", - "value": "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a2020202020202020202020206966206c6574206e6674203d202673656c662e6f776e65644e4654735b69645d2061732026" - }, { - "type": "String", - "value": "2e4e46543f207b0a2020202020202020202020202020202072657475726e206e667420617320267b566965775265736f6c7665722e5265736f6c7665727d0a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d" + "value": "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d20617320267b566965775265736f6c7665722e5265736f6c7665727d3f203f3f206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d" }, { "type": "String", "value": "2e637265617465456d707479436f6c6c656374696f6e286e6674547970653a20547970653c40" diff --git a/cadence/args/bridged-nft-code-chunks-args-previewnet.json b/cadence/args/bridged-nft-code-chunks-args-previewnet.json index 9f368f35..fbbd6f80 100644 --- a/cadence/args/bridged-nft-code-chunks-args-previewnet.json +++ b/cadence/args/bridged-nft-code-chunks-args-previewnet.json @@ -55,10 +55,7 @@ "value": "2e4e46540a0a2020202020202020202020202f2f2061646420746865206e657720746f6b656e20746f207468652064696374696f6e6172792077686963682072656d6f76657320746865206f6c64206f6e650a20202020202020202020202073656c662e65766d4944546f466c6f7749445b746f6b656e2e65766d49445d203d20746f6b656e2e69640a2020202020202020202020206c6574206f6c64546f6b656e203c2d2073656c662e6f776e65644e4654735b746f6b656e2e69645d203c2d20746f6b656e0a0a20202020202020202020202064657374726f79206f6c64546f6b656e0a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320616e206172726179206f66207468652049447320746861742061726520696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e2067657449447328293a205b55496e7436345d207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320616e206172726179206f66207468652045564d2049447320746861742061726520696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e2067657445564d49447328293a205b55496e743235365d207b0a20202020202020202020202072657475726e2073656c662e65766d4944546f466c6f7749442e6b6579730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074686520436164656e6365204e46542e696420666f722074686520676976656e2045564d204e46542049442069660a202020202020202061636365737328616c6c2920766965772066756e20676574436164656e636549442866726f6d2065766d49443a2055496e74323536293a2055496e7436343f207b0a20202020202020202020202072657475726e2073656c662e65766d4944546f466c6f7749445b65766d49445d203f3f2055496e7436342865766d4944290a20202020202020207d0a0a20202020202020202f2f2f2052657475726e73207468652045564d204e4654204944206173736f63696174656420776974682074686520436164656e6365204e46542049442e2054686520676f616c20697320746f20726574726965766520746865204552433732312049442076616c75652e0a20202020202020202f2f2f20417320666172206173207468652062726964676520697320636f6e6365726e65642c20616e2045524337323120646566696e6564206279207468652062726964676520697320746865204e46542773204944206174207468652074696d65206f66206272696467696e670a20202020202020202f2f2f206f72207468652076616c7565206f6620746865204e46542e65766d494420696620697420696d706c656d656e7473207468652043726f7373564d4e46542e45564d4e465420696e74657266616365207768656e20627269646765642e0a20202020202020202f2f2f20466f6c6c6f77696e672074686973207061747465726e2c206966206c6f636b65642c20746865204e465420697320636865636b656420666f722045564d4e465420636f6e666f726d616e63652072657475726e696e67202e65766d494420696620736f2c0a20202020202020202f2f2f206f746865727769736520746865204e465427732049442069732072657475726e656420617320612055496e743235362073696e63652074686174277320686f77207468652062726964676520776f756c642068616e646c65206d696e74696e6720696e207468650a20202020202020202f2f2f20636f72726573706f6e64696e672045524337323120636f6e74726163742e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e2067657445564d49442866726f6d20636164656e636549443a2055496e743634293a2055496e743235363f207b0a2020202020202020202020206966206c6574206e6674203d2073656c662e626f72726f774e465428636164656e6365494429207b0a202020202020202020202020202020206966206c65742065766d4e4654203d2043726f7373564d4e46542e67657445564d49442866726f6d3a206e667429207b0a202020202020202020202020202020202020202072657475726e2065766d4e46540a202020202020202020202020202020207d0a2020202020202020202020202020202072657475726e2055496e74323536286e66742e6964290a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074686520636f6e747261637455524920666f7220746865204e465420636f6c6c656374696f6e20617320646566696e656420696e2074686520736f757263652045524337323120636f6e74726163742e204966206e6f6e65207761730a20202020202020202f2f2f20646566696e6564206174207468652074696d65206f66206272696467696e672c20616e20656d70747920737472696e672069732072657475726e65642e0a202020202020202061636365737328616c6c2920766965772066756e20636f6e747261637455524928293a20537472696e673f207b0a20202020202020202020202072657475726e20" }, { "type": "String", - "value": "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a2020202020202020202020206966206c6574206e6674203d202673656c662e6f776e65644e4654735b69645d2061732026" - }, { - "type": "String", - "value": "2e4e46543f207b0a2020202020202020202020202020202072657475726e206e667420617320267b566965775265736f6c7665722e5265736f6c7665727d0a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d" + "value": "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d20617320267b566965775265736f6c7665722e5265736f6c7665727d3f203f3f206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d" }, { "type": "String", "value": "2e637265617465456d707479436f6c6c656374696f6e286e6674547970653a20547970653c40" diff --git a/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc b/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc index b9b56230..8acafa44 100644 --- a/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc +++ b/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc @@ -244,10 +244,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi /// Borrow the view resolver for the specified NFT ID access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? { - if let nft = &self.ownedNFTs[id] as &{{CONTRACT_NAME}}.NFT? { - return nft as &{ViewResolver.Resolver} - } - return nil + return &self.ownedNFTs[id] as &{ViewResolver.Resolver}? ?? nil } /// Creates an empty collection diff --git a/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc b/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc index 3109d64b..0506affb 100644 --- a/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc +++ b/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc @@ -244,10 +244,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi /// Borrow the view resolver for the specified NFT ID access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? { - if let nft = &self.ownedNFTs[id] as &{{CONTRACT_NAME}}.NFT? { - return nft as &{ViewResolver.Resolver} - } - return nil + return &self.ownedNFTs[id] as &{ViewResolver.Resolver}? ?? nil } /// Creates an empty collection diff --git a/cadence/contracts/templates/testnet/EVMBridgedNFTTemplate.cdc b/cadence/contracts/templates/testnet/EVMBridgedNFTTemplate.cdc new file mode 100644 index 00000000..f90e68be --- /dev/null +++ b/cadence/contracts/templates/testnet/EVMBridgedNFTTemplate.cdc @@ -0,0 +1,380 @@ +import NonFungibleToken from 0x631e88ae7f1d7c20 +import MetadataViews from 0x631e88ae7f1d7c20 +import ViewResolver from 0x631e88ae7f1d7c20 +import FungibleToken from 0x9a0766d93b6608b7 +import FlowToken from 0x7e60df042a9c0868 + +import EVM from 0x8c5303eaa26202d6 + +import ICrossVM from 0xdfc20aee650fcbdf +import IEVMBridgeNFTMinter from 0xdfc20aee650fcbdf +import FlowEVMBridgeNFTEscrow from 0xdfc20aee650fcbdf +import FlowEVMBridgeConfig from 0xdfc20aee650fcbdf +import FlowEVMBridgeUtils from 0xdfc20aee650fcbdf +import FlowEVMBridge from 0xdfc20aee650fcbdf +import CrossVMNFT from 0xdfc20aee650fcbdf + +/// This contract is a template used by FlowEVMBridge to define EVM-native NFTs bridged from Flow EVM to Flow. +/// Upon deployment of this contract, the contract name is derived as a function of the asset type (here an ERC721 aka +/// an NFT) and the contract's EVM address. The derived contract name is then joined with this contract's code, +/// prepared as chunks in FlowEVMBridgeTemplates before being deployed to the Flow EVM Bridge account. +/// +/// On bridging, the ERC721 is transferred to the bridge's CadenceOwnedAccount EVM address and a new NFT is minted from +/// this contract to the bridging caller. On return to Flow EVM, the reverse process is followed - the token is locked +/// in NFT escrow and the ERC721 is transferred to the defined recipient. In this way, the Cadence token acts as a +/// representation of both the EVM NFT and thus ownership rights to it upon bridging back to Flow EVM. +/// +/// To bridge between VMs, a caller can either use the interface exposed on CadenceOwnedAccount or use FlowEVMBridge +/// public contract methods. +/// +access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungibleToken { + + /// Pointer to the Factory deployed Solidity contract address defining the bridged asset + access(all) let evmNFTContractAddress: EVM.EVMAddress + /// Name of the NFT collection defined in the corresponding ERC721 contract + access(all) let name: String + /// Symbol of the NFT collection defined in the corresponding ERC721 contract + access(all) let symbol: String + /// URI of the contract, if available as a var in case the bridge enables cross-VM Metadata syncing in the future + access(all) var contractURI: String? + /// Retain a Collection to reference when resolving Collection Metadata + access(self) let collection: @Collection + /// Mapping of token URIs indexed on their ERC721 ID. This would not normally be retained within a Cadence NFT + /// contract, but since NFT metadata may be updated in EVM, it's retained here so that the bridge can update + /// it against the source ERC721 contract which is treated as the NFT's source of truth. + access(all) let tokenURIs: {UInt256: String} + + /// The NFT resource representing the bridged ERC721 token + /// + access(all) resource NFT : CrossVMNFT.EVMNFT { + /// The Cadence ID of the NFT + access(all) let id: UInt64 + /// The ERC721 ID of the NFT + access(all) let evmID: UInt256 + /// Additional onchain metadata + access(all) let metadata: {String: AnyStruct} + + init( + evmID: UInt256, + metadata: {String: AnyStruct} + ) { + self.id = self.uuid + self.evmID = evmID + self.metadata = metadata + } + + /// Returns the metadata view types supported by this NFT + access(all) view fun getViews(): [Type] { + return [ + Type(), + Type(), + Type(), + Type() + ] + } + + access(all) view fun getName(): String { + return {{CONTRACT_NAME}}.name + } + + access(all) view fun getSymbol(): String { + return {{CONTRACT_NAME}}.symbol + } + + access(all) view fun tokenURI(): String { + return {{CONTRACT_NAME}}.tokenURIs[self.evmID] ?? "" + } + + /// Resolves a metadata view for this NFT + access(all) fun resolveView(_ view: Type): AnyStruct? { + switch view { + // We don't know what kind of file the URI represents (IPFS v HTTP), so we can't resolve Display view + // with the URI as thumbnail - we may a new standard view for EVM NFTs - this is interim + case Type(): + return MetadataViews.EVMBridgedMetadata( + name: self.getName(), + symbol: self.getSymbol(), + uri: MetadataViews.URI(baseURI: nil, value: self.tokenURI()) + ) + case Type(): + return MetadataViews.Serial( + self.id + ) + case Type(): + return {{CONTRACT_NAME}}.resolveContractView( + resourceType: self.getType(), + viewType: Type() + ) + case Type(): + return {{CONTRACT_NAME}}.resolveContractView( + resourceType: self.getType(), + viewType: Type() + ) + } + return nil + } + + /// public function that anyone can call to create a new empty collection + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <- {{CONTRACT_NAME}}.createEmptyCollection(nftType: self.getType()) + } + + /* --- CrossVMNFT conformance --- */ + // + /// Returns the EVM contract address of the NFT + access(all) view fun getEVMContractAddress(): EVM.EVMAddress { + return {{CONTRACT_NAME}}.getEVMContractAddress() + } + } + + /// This resource holds associated NFTs, and serves queries about stored NFTs + access(all) resource Collection : CrossVMNFT.EVMNFTCollection { + /// dictionary of NFT conforming tokens indexed on their ID + access(all) var ownedNFTs: @{UInt64: {NonFungibleToken.NFT}} + /// Mapping of EVM IDs to Flow NFT IDs + access(contract) let evmIDToFlowID: {UInt256: UInt64} + + access(all) var storagePath: StoragePath + access(all) var publicPath: PublicPath + + init () { + self.ownedNFTs <- {} + self.evmIDToFlowID = {} + let collectionData = {{CONTRACT_NAME}}.resolveContractView( + resourceType: Type<@{{CONTRACT_NAME}}.NFT>(), + viewType: Type() + ) as! MetadataViews.NFTCollectionData? + ?? panic("Could not resolve the collection data view for the NFT collection") + self.storagePath = collectionData.storagePath + self.publicPath = collectionData.publicPath + } + + access(all) view fun getName(): String { + return {{CONTRACT_NAME}}.name + } + + access(all) view fun getSymbol(): String { + return {{CONTRACT_NAME}}.symbol + } + + /// Returns a list of NFT types that this receiver accepts + access(all) view fun getSupportedNFTTypes(): {Type: Bool} { + return { Type<@{{CONTRACT_NAME}}.NFT>(): true } + } + + /// Returns whether or not the given type is accepted by the collection + /// A collection that can accept any type should just return true by default + access(all) view fun isSupportedNFTType(type: Type): Bool { + return type == Type<@{{CONTRACT_NAME}}.NFT>() + } + + /// Removes an NFT from the collection and moves it to the caller + access(NonFungibleToken.Withdraw) fun withdraw(withdrawID: UInt64): @{NonFungibleToken.NFT} { + let token <- self.ownedNFTs.remove(key: withdrawID) + ?? panic("Could not withdraw an NFT with the provided ID from the collection") + + return <-token + } + + /// Withdraws an NFT from the collection by its EVM ID + access(NonFungibleToken.Withdraw) fun withdrawByEVMID(_ id: UInt256): @{NonFungibleToken.NFT} { + return <- self.withdraw(withdrawID: + self.getCadenceID(from: id) ?? panic("Could not withdraw an NFT with the provided EVM ID from the collection") + ) + } + + /// Ttakes a NFT and adds it to the collections dictionary and adds the ID to the evmIDToFlowID mapping + access(all) fun deposit(token: @{NonFungibleToken.NFT}) { + let token <- token as! @{{CONTRACT_NAME}}.NFT + + // add the new token to the dictionary which removes the old one + self.evmIDToFlowID[token.evmID] = token.id + let oldToken <- self.ownedNFTs[token.id] <- token + + destroy oldToken + } + + /// Returns an array of the IDs that are in the collection + access(all) view fun getIDs(): [UInt64] { + return self.ownedNFTs.keys + } + + /// Returns an array of the EVM IDs that are in the collection + access(all) view fun getEVMIDs(): [UInt256] { + return self.evmIDToFlowID.keys + } + + /// Returns the Cadence NFT.id for the given EVM NFT ID if + access(all) view fun getCadenceID(from evmID: UInt256): UInt64? { + return self.evmIDToFlowID[evmID] ?? UInt64(evmID) + } + + /// Returns the EVM NFT ID associated with the Cadence NFT ID. The goal is to retrieve the ERC721 ID value. + /// As far as the bridge is concerned, an ERC721 defined by the bridge is the NFT's ID at the time of bridging + /// or the value of the NFT.evmID if it implements the CrossVMNFT.EVMNFT interface when bridged. + /// Following this pattern, if locked, the NFT is checked for EVMNFT conformance returning .evmID if so, + /// otherwise the NFT's ID is returned as a UInt256 since that's how the bridge would handle minting in the + /// corresponding ERC721 contract. + /// + access(all) view fun getEVMID(from cadenceID: UInt64): UInt256? { + if let nft = self.borrowNFT(cadenceID) { + if let evmNFT = CrossVMNFT.getEVMID(from: nft) { + return evmNFT + } + return UInt256(nft.id) + } + return nil + } + + /// Returns the contractURI for the NFT collection as defined in the source ERC721 contract. If none was + /// defined at the time of bridging, an empty string is returned. + access(all) view fun contractURI(): String? { + return {{CONTRACT_NAME}}.contractURI + } + + /// Gets the amount of NFTs stored in the collection + access(all) view fun getLength(): Int { + return self.ownedNFTs.keys.length + } + + /// Retrieves a reference to the NFT stored in the collection by its ID + access(all) view fun borrowNFT(_ id: UInt64): &{NonFungibleToken.NFT}? { + return &self.ownedNFTs[id] + } + + /// Borrow the view resolver for the specified NFT ID + access(all) view fun borrowViewResolver(id: UInt64): &{ViewResolver.Resolver}? { + return &self.ownedNFTs[id] as &{ViewResolver.Resolver}? ?? nil + } + + /// Creates an empty collection + access(all) fun createEmptyCollection(): @{NonFungibleToken.Collection} { + return <-{{CONTRACT_NAME}}.createEmptyCollection(nftType: Type<@{{CONTRACT_NAME}}.NFT>()) + } + } + + /// createEmptyCollection creates an empty Collection for the specified NFT type + /// and returns it to the caller so that they can own NFTs + access(all) fun createEmptyCollection(nftType: Type): @{NonFungibleToken.Collection} { + return <- create Collection() + } + + /********************** + Getters + ***********************/ + + /// Returns the EVM contract address of the NFT this contract represents + /// + access(all) view fun getEVMContractAddress(): EVM.EVMAddress { + return self.evmNFTContractAddress + } + + /// Function that returns all the Metadata Views implemented by a Non Fungible Token + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getContractViews(resourceType: Type?): [Type] { + return [ + Type(), + Type(), + Type() + ] + } + + /// Function that resolves a metadata view for this contract. + /// + /// @param view: The Type of the desired view. + /// @return A structure representing the requested view. + /// + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { + case Type(): + let identifier = "{{CONTRACT_NAME}}Collection" + let collectionData = MetadataViews.NFTCollectionData( + storagePath: StoragePath(identifier: identifier)!, + publicPath: PublicPath(identifier: identifier)!, + publicCollection: Type<&{{CONTRACT_NAME}}.Collection>(), + publicLinkedType: Type<&{{CONTRACT_NAME}}.Collection>(), + createEmptyCollectionFunction: (fun(): @{NonFungibleToken.Collection} { + return <-{{CONTRACT_NAME}}.createEmptyCollection(nftType: Type<@{{CONTRACT_NAME}}.NFT>()) + }) + ) + return collectionData + case Type(): + let media = MetadataViews.Media( + file: MetadataViews.HTTPFile( + url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" + ), + mediaType: "image/svg+xml" + ) + return MetadataViews.NFTCollectionDisplay( + name: "The FlowVM Bridged NFT Collection", + description: "This collection was bridged from Flow EVM.", + externalURL: MetadataViews.ExternalURL("https://bridge.flow.com/nft"), + squareImage: media, + bannerImage: media, + socials: {} + ) + case Type(): + return MetadataViews.EVMBridgedMetadata( + name: self.name, + symbol: self.symbol, + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") + ) + } + return nil + } + + /********************** + Internal Methods + ***********************/ + + /// Allows the bridge to mint NFTs from bridge-defined NFT contracts + /// + access(account) + fun mintNFT(id: UInt256, tokenURI: String): @NFT { + pre { + self.tokenURIs[id] == nil: "A token with the given ERC721 ID already exists" + } + self.tokenURIs[id] = tokenURI + return <-create NFT( + evmID: id, + metadata: { + "Bridged Block": getCurrentBlock().height, + "Bridged Timestamp": getCurrentBlock().timestamp + } + ) + } + + /// Allows the bridge to update the URI of bridged NFTs. This assumes that the EVM-defining project may contain + /// logic (onchain or offchain) which updates NFT metadata in the source ERC721 contract. On bridging, the URI can + /// then be updated in this contract to reflect the source ERC721 contract's metadata. + /// + access(account) + fun updateTokenURI(evmID: UInt256, newURI: String) { + pre { + self.tokenURIs[evmID] != nil: "No token with the given ERC721 ID exists" + } + if self.tokenURIs[evmID] != newURI { + self.tokenURIs[evmID] = newURI + } + } + + init(name: String, symbol: String, evmContractAddress: EVM.EVMAddress, contractURI: String?) { + self.evmNFTContractAddress = evmContractAddress + self.name = name + self.symbol = symbol + self.contractURI = contractURI + self.tokenURIs = {} + self.collection <- create Collection() + + FlowEVMBridgeConfig.associateType(Type<@{{CONTRACT_NAME}}.NFT>(), with: self.evmNFTContractAddress) + FlowEVMBridgeNFTEscrow.initializeEscrow( + forType: Type<@{{CONTRACT_NAME}}.NFT>(), + name: name, + symbol: symbol, + erc721Address: self.evmNFTContractAddress + ) + } +} diff --git a/cadence/contracts/templates/testnet/EVMBridgedTokenTemplate.cdc b/cadence/contracts/templates/testnet/EVMBridgedTokenTemplate.cdc new file mode 100644 index 00000000..b2d82fd3 --- /dev/null +++ b/cadence/contracts/templates/testnet/EVMBridgedTokenTemplate.cdc @@ -0,0 +1,271 @@ +import NonFungibleToken from 0x631e88ae7f1d7c20 +import MetadataViews from 0x631e88ae7f1d7c20 +import FungibleTokenMetadataViews from 0x9a0766d93b6608b7 +import ViewResolver from 0x631e88ae7f1d7c20 +import FungibleToken from 0x9a0766d93b6608b7 +import FlowToken from 0x7e60df042a9c0868 + +import EVM from 0x8c5303eaa26202d6 + +import ICrossVM from 0xdfc20aee650fcbdf +import IEVMBridgeTokenMinter from 0xdfc20aee650fcbdf +import FlowEVMBridgeTokenEscrow from 0xdfc20aee650fcbdf +import FlowEVMBridgeConfig from 0xdfc20aee650fcbdf +import FlowEVMBridgeUtils from 0xdfc20aee650fcbdf +import FlowEVMBridge from 0xdfc20aee650fcbdf +import CrossVMNFT from 0xdfc20aee650fcbdf +import CrossVMToken from 0xdfc20aee650fcbdf + +/// This contract is a template used by FlowEVMBridge to define EVM-native fungible tokens bridged from Flow EVM to +/// Cadence. Upon deployment of this contract, the contract name is derived as a function of the asset type (here an +/// ERC20) and the contract's EVM address. The derived contract name is then joined with this contract's code, +/// prepared as chunks in FlowEVMBridgeTemplates before being deployed to the Flow EVM Bridge account. +/// +/// On bridging, the ERC20 is transferred to the bridge's CadenceOwnedAccount EVM address and tokens are minted from +/// this contract to the bridging caller. On return to Flow EVM, the reverse process is followed - the token is burned +/// in this contract and the ERC20 is transferred to the defined recipient. In this way, the Cadence Vault acts as a +/// representation of both the EVM tokens and thus ownership rights to it upon bridging back to Flow EVM. +/// +/// To bridge between VMs, a caller can either use the interface exposed on CadenceOwnedAccount or use FlowEVMBridge +/// public contract methods. +/// +access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, FungibleToken { + + /// Pointer to the Factory deployed Solidity contract address defining the bridged asset + access(all) let evmTokenContractAddress: EVM.EVMAddress + /// Name of the fungible token defined in the corresponding ERC20 contract + access(all) let name: String + /// Symbol of the fungible token defined in the corresponding ERC20 contract + access(all) let symbol: String + /// Decimal place value defined in the source ERC20 contract + access(all) let decimals: UInt8 + /// URI of the contract, if available as a var in case the bridge enables cross-VM Metadata syncing in the future + access(all) var contractURI: String? + /// Total supply of this Cadence token in circulation + /// NOTE: This does not reflect the total supply of the source ERC20 in circulation within EVM + access(all) var totalSupply: UFix64 + /// Retain a Vault to reference when resolving Vault Metadata + access(self) let vault: @Vault + + /// The Vault resource representing the bridged ERC20 token + /// + access(all) resource Vault : CrossVMToken.EVMTokenInfo, FungibleToken.Vault { + /// Balance of the tokens in a given Vault + access(all) var balance: UFix64 + + init(balance: UFix64) { + self.balance = balance + } + + /* --- CrossVMToken.EVMFTVault conformance --- */ + // + /// Gets the ERC20 name value + access(all) view fun getName(): String { + return {{CONTRACT_NAME}}.name + } + /// Gets the ERC20 symbol value + access(all) view fun getSymbol(): String { + return {{CONTRACT_NAME}}.symbol + } + /// Gets the ERC20 decimals value + access(all) view fun getDecimals(): UInt8 { + return {{CONTRACT_NAME}}.decimals + } + /// Returns the EVM contract address of the fungible token + access(all) view fun getEVMContractAddress(): EVM.EVMAddress { + return {{CONTRACT_NAME}}.getEVMContractAddress() + } + + access(all) view fun getViews(): [Type] { + return {{CONTRACT_NAME}}.getContractViews(resourceType: nil) + } + + access(all) fun resolveView(_ view: Type): AnyStruct? { + return {{CONTRACT_NAME}}.resolveContractView(resourceType: nil, viewType: view) + } + + /// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts + access(all) view fun getSupportedVaultTypes(): {Type: Bool} { + return { self.getType(): true } + } + + access(all) view fun isSupportedVaultType(type: Type): Bool { + return self.getSupportedVaultTypes()[type] ?? false + } + + /// Asks if the amount can be withdrawn from this vault + access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool { + return amount <= self.balance + } + + /// deposit + /// + /// Function that takes a Vault object as an argument and adds + /// its balance to the balance of the owners Vault. + /// + /// It is allowed to destroy the sent Vault because the Vault + /// was a temporary holder of the tokens. The Vault's balance has + /// been consumed and therefore can be destroyed. + /// + access(all) fun deposit(from: @{FungibleToken.Vault}) { + let vault <- from as! @Vault + self.balance = self.balance + vault.balance + vault.balance = 0.0 + destroy vault + } + + /// createEmptyVault + /// + /// Function that creates a new Vault with a balance of zero + /// and returns it to the calling context. A user must call this function + /// and store the returned Vault in their storage in order to allow their + /// account to be able to receive deposits of this token type. + /// + access(all) fun createEmptyVault(): @Vault { + return <-create Vault(balance: 0.0) + } + + /// withdraw + /// + /// Function that takes an amount as an argument + /// and withdraws that amount from the Vault. + /// + /// It creates a new temporary Vault that is used to hold + /// the tokens that are being transferred. It returns the newly + /// created Vault to the context that called so it can be deposited + /// elsewhere. + /// + access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @Vault { + self.balance = self.balance - amount + return <-create Vault(balance: amount) + } + + /// Called when a fungible token is burned via the `Burner.burn()` method + access(contract) fun burnCallback() { + if self.balance > 0.0 { + {{CONTRACT_NAME}}.totalSupply = {{CONTRACT_NAME}}.totalSupply - self.balance + } + self.balance = 0.0 + } + } + + /// createEmptyVault + /// + /// Function that creates a new Vault with a balance of zero and returns it to the calling context. A user must call + /// this function and store the returned Vault in their storage in order to allow their account to be able to + /// receive deposits of this token type. + /// + access(all) fun createEmptyVault(vaultType: Type): @{{CONTRACT_NAME}}.Vault { + return <- create Vault(balance: 0.0) + } + + /********************** + Getters + ***********************/ + + /// Returns the EVM contract address of the fungible token this contract represents + /// + access(all) view fun getEVMContractAddress(): EVM.EVMAddress { + return self.evmTokenContractAddress + } + + /// Function that returns all the Metadata Views implemented by this fungible token contract. + /// + /// @return An array of Types defining the implemented views. This value will be used by + /// developers to know which parameter to pass to the resolveView() method. + /// + access(all) view fun getContractViews(resourceType: Type?): [Type] { + return [ + Type(), + Type(), + Type(), + Type(), + Type() + ] + } + + /// Function that resolves a metadata view for this contract. + /// + /// @param view: The Type of the desired view. + /// + /// @return A structure representing the requested view. + /// + access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? { + switch viewType { + case Type(): + return FungibleTokenMetadataViews.FTView( + ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTDisplay?, + ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type()) as! FungibleTokenMetadataViews.FTVaultData? + ) + case Type(): + let media = MetadataViews.Media( + file: MetadataViews.HTTPFile( + url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg" + ), + mediaType: "image/svg+xml" + ) + let medias = MetadataViews.Medias([media]) + return FungibleTokenMetadataViews.FTDisplay( + // TODO: Decide on how we want to represent bridged token media + name: self.name, + symbol: self.symbol, + description: "This fungible token was bridged from Flow EVM.", + externalURL: MetadataViews.ExternalURL("https://bridge.flow.com/fungible-token"), + logos: medias, + socials: {} + ) + case Type(): + return FungibleTokenMetadataViews.FTVaultData( + storagePath: /storage/{{CONTRACT_NAME}}Vault, + receiverPath: /public/{{CONTRACT_NAME}}Receiver, + metadataPath: /public/{{CONTRACT_NAME}}Vault, + receiverLinkedType: Type<&{{CONTRACT_NAME}}.Vault>(), + metadataLinkedType: Type<&{{CONTRACT_NAME}}.Vault>(), + createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} { + return <-self.createEmptyVault(vaultType: Type<@{{CONTRACT_NAME}}.Vault>()) + }) + ) + case Type(): + return FungibleTokenMetadataViews.TotalSupply( + totalSupply: self.totalSupply + ) + case Type(): + return MetadataViews.EVMBridgedMetadata( + name: self.name, + symbol: self.symbol, + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") + ) + } + return nil + } + + /********************** + Internal Methods + ***********************/ + + /// Allows the bridge to mint tokens from bridge-defined fungible token contracts + /// + access(account) fun mintTokens(amount: UFix64): @{FungibleToken.Vault} { + self.totalSupply = self.totalSupply + amount + return <- create Vault(balance: amount) + } + + init(name: String, symbol: String, decimals: UInt8, evmContractAddress: EVM.EVMAddress, contractURI: String?) { + self.evmTokenContractAddress = evmContractAddress + self.name = name + self.symbol = symbol + self.decimals = decimals + self.contractURI = contractURI + self.totalSupply = 0.0 + self.vault <- create Vault(balance: 0.0) + + FlowEVMBridgeConfig.associateType(Type<@{{CONTRACT_NAME}}.Vault>(), with: self.evmTokenContractAddress) + FlowEVMBridgeTokenEscrow.initializeEscrow( + with: <-create Vault(balance: 0.0), + name: name, + symbol: symbol, + decimals: decimals, + evmTokenAddress: self.evmTokenContractAddress + ) + } +} From e41d781522985425d3f0c924f755d26beeb81ad1 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:22:25 -0600 Subject: [PATCH 2/6] update NFT standard --- cadence/contracts/standards/NonFungibleToken.cdc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cadence/contracts/standards/NonFungibleToken.cdc b/cadence/contracts/standards/NonFungibleToken.cdc index efce1090..6e707756 100644 --- a/cadence/contracts/standards/NonFungibleToken.cdc +++ b/cadence/contracts/standards/NonFungibleToken.cdc @@ -8,6 +8,17 @@ The interface that all Non-Fungible Token contracts should conform to. If a user wants to deploy a new NFT contract, their contract should implement The types defined here +/// Contributors (please add to this list if you contribute!): +/// - Joshua Hannan - https://github.com/joshuahannan +/// - Bastian Müller - https://twitter.com/turbolent +/// - Dete Shirley - https://twitter.com/dete73 +/// - Bjarte Karlsen - https://twitter.com/0xBjartek +/// - Austin Kline - https://twitter.com/austin_flowty +/// - Giovanni Sanchez - https://twitter.com/gio_incognito +/// - Deniz Edincik - https://twitter.com/bluesign +/// +/// Repo reference: https://github.com/onflow/flow-nft + ## `NFT` resource interface The core resource type that represents an NFT in the smart contract. @@ -105,7 +116,7 @@ access(all) contract interface NonFungibleToken: ViewResolver { /// Gets all the NFTs that this NFT directly owns /// @return A dictionary of all subNFTS keyed by type - access(all) view fun getAvailableSubNFTS(): {Type: UInt64} { + access(all) view fun getAvailableSubNFTS(): {Type: [UInt64]} { return {} } @@ -238,4 +249,4 @@ access(all) contract interface NonFungibleToken: ViewResolver { result.getIDs().length == 0: "The created collection must be empty!" } } -} +} \ No newline at end of file From fac0e0f39a7d26f8ade624797e2f1826120c7b1f Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Wed, 5 Jun 2024 17:05:58 -0600 Subject: [PATCH 3/6] add collection & vault configuration scripts --- .../scripts/nft/has_collection_configured.cdc | 30 +++++++++++++++++++ .../scripts/tokens/has_vault_configured.cdc | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 cadence/scripts/nft/has_collection_configured.cdc create mode 100644 cadence/scripts/tokens/has_vault_configured.cdc diff --git a/cadence/scripts/nft/has_collection_configured.cdc b/cadence/scripts/nft/has_collection_configured.cdc new file mode 100644 index 00000000..f22685cc --- /dev/null +++ b/cadence/scripts/nft/has_collection_configured.cdc @@ -0,0 +1,30 @@ +import "NonFungibleToken" +import "MetadataViews" +import "ViewResolver" + +import "FlowEVMBridgeUtils" + +/// Returns true if the recipient has Collection configured for the provided NFT contract +/// +/// @param nftIdentifier The type identifier of the NFT Collection to check for +/// @param recipient The address of the recipient +/// +/// @returns true if the recipient has Collection configured for the provided NFT contract, false if not. Reverts if the +/// provided contract cannot be accessed or does not have default Collection storage information. +/// +access(all) +fun main(nftIdentifier: String, recipient: Address): Bool { + let nftType = CompositeType(nftIdentifier) ?? panic("Invalid nft identifier: ".concat(nftIdentifier)) + let contractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: nftType) + ?? panic("Could not find contract address for nft: ".concat(nftIdentifier)) + let contractName = FlowEVMBridgeUtils.getContractName(fromType: nftType) + ?? panic("Could not find contract name for nft: ".concat(nftIdentifier)) + let nftContract = getAccount(contractAddress).contracts.borrow<&{NonFungibleToken}>(name: contractName) + ?? panic("No such contract found") + let collectionData = nftContract.resolveContractView( + resourceType: nftType, + viewType: Type() + ) as! MetadataViews.NFTCollectionData? + ?? panic("FungibleToken does not provide default Collection data") + return getAccount(recipient).capabilities.exists(collectionData.publicPath) +} diff --git a/cadence/scripts/tokens/has_vault_configured.cdc b/cadence/scripts/tokens/has_vault_configured.cdc new file mode 100644 index 00000000..07643442 --- /dev/null +++ b/cadence/scripts/tokens/has_vault_configured.cdc @@ -0,0 +1,30 @@ +import "FungibleToken" +import "FungibleTokenMetadataViews" +import "ViewResolver" + +import "FlowEVMBridgeUtils" + +/// Returns true if the recipient has Vault configured for the provided FungibleToken contract +/// +/// @param vaultIdentifier The type identifier of the Vault to check for +/// @param recipient The address of the recipient +/// +/// @returns true if the recipient has Vault configured for the provided FungibleToken contract, false if not. Reverts +/// if the provided contract cannot be accessed or does not have default Vault storage information. +/// +access(all) +fun main(vaultIdentifier: String, recipient: Address): Bool { + let vaultType = CompositeType(vaultIdentifier) ?? panic("Invalid vault identifier: ".concat(vaultIdentifier)) + let contractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: vaultType) + ?? panic("Could not find contract address for vault: ".concat(vaultIdentifier)) + let contractName = FlowEVMBridgeUtils.getContractName(fromType: vaultType) + ?? panic("Could not find contract name for vault: ".concat(vaultIdentifier)) + let tokenContract = getAccount(contractAddress).contracts.borrow<&{FungibleToken}>(name: contractName) + ?? panic("No such contract found") + let vaultData = tokenContract.resolveContractView( + resourceType: vaultType, + viewType: Type() + ) as! FungibleTokenMetadataViews.FTVaultData? + ?? panic("FungibleToken does not provide default Vault data") + return getAccount(recipient).capabilities.exists(vaultData.receiverPath) +} From f3538cf09714cd592c817e0a2f484b8a9d1019a2 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Fri, 7 Jun 2024 10:14:09 -0600 Subject: [PATCH 4/6] add Flow port supporting Vault summary script --- .../get_all_vault_info_from_storage.cdc | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 cadence/scripts/tokens/get_all_vault_info_from_storage.cdc diff --git a/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc b/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc new file mode 100644 index 00000000..b945c83d --- /dev/null +++ b/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc @@ -0,0 +1,99 @@ +import "FungibleToken" +import "FlowToken" +import "MetadataViews" +import "FungibleTokenMetadataViews" + +access(all) +struct FTVaultInfo { + access(all) let name: String + access(all) let symbol: String + access(all) var balance: UFix64 + access(all) let tokenContractAddress: Address + access(all) let tokenContractName: String + access(all) let storagePath: StoragePath + access(all) let receiverPath: PublicPath + + init( + name: String, + symbol: String, + balance: UFix64, + tokenContractAddress: Address, + tokenContractName: String, + storagePath: StoragePath, + receiverPath: PublicPath + ) { + self.name = name + self.symbol = symbol + self.balance = balance + self.tokenContractAddress = tokenContractAddress + self.tokenContractName = tokenContractName + self.storagePath = storagePath + self.receiverPath = receiverPath + } + + access(all) fun updateBalance(delta: UFix64) { + self.balance = self.balance + delta + } +} + +access(all) +fun getVaultInfo( + vaultType: Type, + balance: UFix64, + display: FungibleTokenMetadataViews.FTDisplay, + data: FungibleTokenMetadataViews.FTVaultData +): FTVaultInfo { + let identifier = vaultType.identifier + let addrString = "0x".concat(identifier.split(separator: ".")[1]) + let contractAddress = Address.fromString(addrString) ?? panic("INVALID ADDRESS: ".concat(addrString)) + let contractName = identifier.split(separator: ".")[2] + + return FTVaultInfo( + name: display.name, + symbol: display.symbol, + balance: balance, + tokenContractAddress: contractAddress, + tokenContractName: contractName, + storagePath: data.storagePath, + receiverPath: data.receiverPath + ) +} + +access(all) +fun main(address: Address): {Type: FTVaultInfo} { + let acct = getAuthAccount(address) + let res: {Type: FTVaultInfo} = {} + + // Define target types + let ftVaultType = Type<@{FungibleToken.Vault}>() + let displayType = Type() + let dataType = Type() + + acct.storage.forEachStored(fun (path: StoragePath, type: Type): Bool { + if type.isSubtype(of: ftVaultType) { + // Reference the Vault at the current storage path + let vault = acct.storage.borrow<&{FungibleToken.Vault}>(from: path) + ?? panic("Problem borrowing vault from path: ".concat(path.toString())) + // Get the balance + var balance = vault.balance + // Update the balance if the Vault type has already been encountered & return early + if let info = res[type] { + info.updateBalance(delta: balance) + return true + } + + // Resolve FT metadata views + let display = vault.resolveView(displayType) as! FungibleTokenMetadataViews.FTDisplay? + let data = vault.resolveView(dataType) as! FungibleTokenMetadataViews.FTVaultData? + // Continue if metadata views are not resolved - no relevant info to capture + if display == nil || data == nil { + return true + } + // Capture the relevant info and insert to our result mapping + let info = getVaultInfo(vaultType: type, balance: balance, display: display!, data: data!) + res.insert(key: type, info) + } + return true + }) + return res +} \ No newline at end of file From ba7e726bb05ef9c37c40671e31bf67acfd01cdd3 Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:38:24 -0600 Subject: [PATCH 5/6] add comments to token query --- .../get_all_vault_info_from_storage.cdc | 23 +++++++++++++++++++ .../utils/get_evm_address_from_hex.cdc | 8 +++++++ 2 files changed, 31 insertions(+) create mode 100644 cadence/scripts/utils/get_evm_address_from_hex.cdc diff --git a/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc b/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc index b945c83d..b6d4f117 100644 --- a/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc +++ b/cadence/scripts/tokens/get_all_vault_info_from_storage.cdc @@ -3,14 +3,22 @@ import "FlowToken" import "MetadataViews" import "FungibleTokenMetadataViews" +/// Custom struct to store Fungible Token vault info access(all) struct FTVaultInfo { + /// The name of the Fungible Token access(all) let name: String + /// The symbol of the Fungible Token access(all) let symbol: String + /// The balance of the Fungible Token access(all) var balance: UFix64 + /// The address of the Fungible Token contract access(all) let tokenContractAddress: Address + /// The name of the Fungible Token contract access(all) let tokenContractName: String + /// The storage path of the Fungible Token vault access(all) let storagePath: StoragePath + /// The receiver path of the Fungible Token vault access(all) let receiverPath: PublicPath init( @@ -31,11 +39,21 @@ struct FTVaultInfo { self.receiverPath = receiverPath } + /// Updates the balance of the Fungible Token vault access(all) fun updateBalance(delta: UFix64) { self.balance = self.balance + delta } } +/// Returns a FTVaultInfo struct with the provided data +/// +/// @param vaultType: The type of the Fungible Token vault +/// @param balance: The balance of the Fungible Token vault +/// @param display: The FTDisplay view of the Fungible Token vault +/// @param data: The FTVaultData view of the Fungible Token vault +/// +/// @return FTVaultInfo: The FTVaultInfo struct with the provided data +/// access(all) fun getVaultInfo( vaultType: Type, @@ -59,6 +77,11 @@ fun getVaultInfo( ) } +/// Returns a mapping of all Fungible Token vaults stored in the account's storage indexed by their type +/// +/// @param address: The address of the account to query +/// +/// @return {Type: FTVaultInfo}: A mapping of vault types to their respective info access(all) fun main(address: Address): {Type: FTVaultInfo} { let acct = getAuthAccount(address) diff --git a/cadence/scripts/utils/get_evm_address_from_hex.cdc b/cadence/scripts/utils/get_evm_address_from_hex.cdc new file mode 100644 index 00000000..b58ac9e0 --- /dev/null +++ b/cadence/scripts/utils/get_evm_address_from_hex.cdc @@ -0,0 +1,8 @@ +import "EVM" + +import "EVMUtils" + +access(all) +fun main(hex: String): EVM.EVMAddress? { + return EVMUtils.getEVMAddressFromHexString(address: hex) +} \ No newline at end of file From 45f058fdee9369e82881b9c5e1b353d776ce26bd Mon Sep 17 00:00:00 2001 From: Giovanni Sanchez <108043524+sisyphusSmiling@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:39:03 -0600 Subject: [PATCH 6/6] add test coverage for vault & collection config queries --- cadence/tests/flow_evm_bridge_tests.cdc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cadence/tests/flow_evm_bridge_tests.cdc b/cadence/tests/flow_evm_bridge_tests.cdc index d7f01472..b63b111a 100644 --- a/cadence/tests/flow_evm_bridge_tests.cdc +++ b/cadence/tests/flow_evm_bridge_tests.cdc @@ -413,6 +413,12 @@ fun testMintExampleNFTSucceeds() { alice ) Test.expect(setupCollectionResult, Test.beSucceeded()) + let hasCollection = executeScript( + "../scripts/nft/has_collection_configured.cdc", + [exampleNFTIdentifier, alice.address] + ) + Test.expect(hasCollection, Test.beSucceeded()) + Test.assertEqual(true, hasCollection.returnValue as! Bool? ?? panic("Problem getting collection status")) let mintExampleNFTResult = executeTransaction( "../transactions/example-assets/example-nft/mint_nft.cdc", @@ -440,6 +446,12 @@ fun testMintExampleTokenSucceeds() { alice ) Test.expect(setupVaultResult, Test.beSucceeded()) + let hasVault = executeScript( + "../scripts/tokens/has_vault_configured.cdc", + [exampleTokenIdentifier, alice.address] + ) + Test.expect(hasVault, Test.beSucceeded()) + Test.assertEqual(true, hasVault.returnValue as! Bool? ?? panic("Problem getting vault status")) let mintExampleTokenResult = executeTransaction( "../transactions/example-assets/example-token/mint_tokens.cdc",