From 6573c1f26ee5e1f0153bbadf5b6cbe1c5a9baae7 Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Mon, 26 Jun 2023 19:33:25 +0530 Subject: [PATCH 01/30] Added IDO Program Milestone Project --- programs/ido-program/.gitignore | 7 + programs/ido-program/Anchor.toml | 15 + programs/ido-program/Cargo.lock | 2403 +++++++++++++++++ programs/ido-program/Cargo.toml | 13 + programs/ido-program/migrations/deploy.ts | 12 + programs/ido-program/package.json | 20 + .../programs/ido-program/Cargo.toml | 20 + .../programs/ido-program/Xargo.toml | 2 + .../programs/ido-program/src/lib.rs | 438 +++ programs/ido-program/tests/ido-program.ts | 466 ++++ programs/ido-program/tsconfig.json | 11 + programs/ido-program/yarn.lock | 1188 ++++++++ src/SUMMARY.md | 2 +- .../milestone_project_ido-program.md | 1319 +++++++++ 14 files changed, 5915 insertions(+), 1 deletion(-) create mode 100644 programs/ido-program/.gitignore create mode 100644 programs/ido-program/Anchor.toml create mode 100644 programs/ido-program/Cargo.lock create mode 100644 programs/ido-program/Cargo.toml create mode 100644 programs/ido-program/migrations/deploy.ts create mode 100644 programs/ido-program/package.json create mode 100644 programs/ido-program/programs/ido-program/Cargo.toml create mode 100644 programs/ido-program/programs/ido-program/Xargo.toml create mode 100644 programs/ido-program/programs/ido-program/src/lib.rs create mode 100644 programs/ido-program/tests/ido-program.ts create mode 100644 programs/ido-program/tsconfig.json create mode 100644 programs/ido-program/yarn.lock create mode 100644 src/anchor_in_depth/milestone_project_ido-program.md diff --git a/programs/ido-program/.gitignore b/programs/ido-program/.gitignore new file mode 100644 index 0000000..d243ecc --- /dev/null +++ b/programs/ido-program/.gitignore @@ -0,0 +1,7 @@ + +.anchor +.DS_Store +target +**/*.rs.bk +node_modules +test-ledger diff --git a/programs/ido-program/Anchor.toml b/programs/ido-program/Anchor.toml new file mode 100644 index 0000000..73c3e78 --- /dev/null +++ b/programs/ido-program/Anchor.toml @@ -0,0 +1,15 @@ +[features] +seeds = false +skip-lint = false +[programs.localnet] +ido_program = "GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6" + +[registry] +url = "https://api.apr.dev" + +[provider] +cluster = "Localnet" +wallet = "/Users/catrovacer/.config/solana/id.json" + +[scripts] +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/programs/ido-program/Cargo.lock b/programs/ido-program/Cargo.lock new file mode 100644 index 0000000..6d83aeb --- /dev/null +++ b/programs/ido-program/Cargo.lock @@ -0,0 +1,2403 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589c637f0e68c877bbd59a4599bbe849cac8e5f3e4b5a3ebae8f528cd218dcdc" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom 0.2.10", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anchor-attribute-access-control" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa5be5b72abea167f87c868379ba3c2be356bfca9e6f474fd055fa0f7eeb4f2" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-account" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f468970344c7c9f9d03b4da854fd7c54f21305059f53789d0045c1dd803f0018" +dependencies = [ + "anchor-syn", + "anyhow", + "bs58 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-constant" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59948e7f9ef8144c2aefb3f32a40c5fce2798baeec765ba038389e82301017ef" +dependencies = [ + "anchor-syn", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-error" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc753c9d1c7981cb8948cf7e162fb0f64558999c0413058e2d43df1df5448086" +dependencies = [ + "anchor-syn", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-event" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38b4e172ba1b52078f53fdc9f11e3dc0668ad27997838a0aad2d148afac8c97" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-attribute-program" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eebd21543606ab61e2d83d9da37d24d3886a49f390f9c43a1964735e8c0f0d5" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-accounts" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4720d899b3686396cced9508f23dab420f1308344456ec78ef76f98fda42af" +dependencies = [ + "anchor-syn", + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-space" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f495e85480bd96ddeb77b71d499247c7d4e8b501e75ecb234e9ef7ae7bd6552a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-lang" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2d4b20100f1310a774aba3471ef268e5c4ba4d5c28c0bbe663c2658acbc414" +dependencies = [ + "anchor-attribute-access-control", + "anchor-attribute-account", + "anchor-attribute-constant", + "anchor-attribute-error", + "anchor-attribute-event", + "anchor-attribute-program", + "anchor-derive-accounts", + "anchor-derive-space", + "arrayref", + "base64 0.13.1", + "bincode", + "borsh 0.10.3", + "bytemuck", + "getrandom 0.2.10", + "solana-program", + "thiserror", +] + +[[package]] +name = "anchor-spl" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f860599da1c2354e7234c768783049eb42e2f54509ecfc942d2e0076a2da7b" +dependencies = [ + "anchor-lang", + "solana-program", + "spl-associated-token-account", + "spl-token", + "spl-token-2022", +] + +[[package]] +name = "anchor-syn" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a125e4b0cc046cfec58f5aa25038e34cf440151d58f0db3afc55308251fe936d" +dependencies = [ + "anyhow", + "bs58 0.5.0", + "heck", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.6", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "array-bytes" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ad284aeb45c13f2fb4f084de4a420ebf447423bdf9386c0540ce33cb3ef4b8c" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "blake3" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive 0.9.3", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive 0.10.3", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal 0.9.3", + "borsh-schema-derive-internal 0.9.3", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal 0.10.3", + "borsh-schema-derive-internal 0.10.3", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "num-traits", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset 0.8.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.18", +] + +[[package]] +name = "darling_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.6", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "serde", + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "ido-program" +version = "0.1.0" +dependencies = [ + "anchor-lang", + "anchor-spl", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "rayon", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.146" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "solana-frozen-abi" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec1a23136ef6072f3c6e3eabf6349dc0219ae23129c44754a4f9f8506941cd" +dependencies = [ + "ahash 0.8.3", + "blake3", + "block-buffer 0.10.4", + "bs58 0.4.0", + "bv", + "byteorder", + "cc", + "either", + "generic-array", + "getrandom 0.1.16", + "im", + "lazy_static", + "log", + "memmap2", + "once_cell", + "rand_core 0.6.4", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.6", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89744bbdcb6dc87805bf50f3db4520adf56b9fdd2a55b80b8de3b4c97b5c4bc8" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.18", +] + +[[package]] +name = "solana-logger" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc342d61b86066ca7bb72ab4ba17f677b8c2c3a3e71fb9286a57097f3b36b85" +dependencies = [ + "env_logger", + "lazy_static", + "log", +] + +[[package]] +name = "solana-program" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf210d64911289b4a76cbd1fd0bfc6ab1d08e9d568a2dd021d03835d2e7efb6" +dependencies = [ + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "array-bytes", + "base64 0.21.2", + "bincode", + "bitflags", + "blake3", + "borsh 0.10.3", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.10", + "itertools", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1", + "log", + "memoffset 0.9.0", + "num-bigint", + "num-derive", + "num-traits", + "parking_lot", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.6", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-sdk" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a43b1c03aa69e1410ada4b5b2971ab948806c188015ac54684deec67d2739a" +dependencies = [ + "assert_matches", + "base64 0.21.2", + "bincode", + "bitflags", + "borsh 0.10.3", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.7", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array", + "hmac 0.12.1", + "itertools", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memmap2", + "num-derive", + "num-traits", + "num_enum 0.6.1", + "pbkdf2 0.11.0", + "qstring", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "serde_with", + "sha2 0.10.6", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aec66b2d42de3e7a90086ca6ec16f66ac0019bfc3a6ca44ade2404a9dc8c128a" +dependencies = [ + "bs58 0.4.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.18", +] + +[[package]] +name = "solana-zk-token-sdk" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34a565bea0147718f57ee8bbd79438341769acd53f634d665c34114040ea7c26" +dependencies = [ + "aes-gcm-siv", + "arrayref", + "base64 0.21.2", + "bincode", + "bytemuck", + "byteorder", + "curve25519-dalek", + "getrandom 0.1.16", + "itertools", + "lazy_static", + "merlin", + "num-derive", + "num-traits", + "rand 0.7.3", + "serde", + "serde_json", + "sha3 0.9.1", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "spl-associated-token-account" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" +dependencies = [ + "assert_matches", + "borsh 0.9.3", + "num-derive", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "spl-memo" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-token" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum 0.5.11", + "solana-program", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive", + "num-traits", + "num_enum 0.5.11", + "solana-program", + "solana-zk-token-sdk", + "spl-memo", + "spl-token", + "thiserror", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] diff --git a/programs/ido-program/Cargo.toml b/programs/ido-program/Cargo.toml new file mode 100644 index 0000000..ef17a63 --- /dev/null +++ b/programs/ido-program/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] +members = [ + "programs/*" +] + +[profile.release] +overflow-checks = true +lto = "fat" +codegen-units = 1 +[profile.release.build-override] +opt-level = 3 +incremental = false +codegen-units = 1 diff --git a/programs/ido-program/migrations/deploy.ts b/programs/ido-program/migrations/deploy.ts new file mode 100644 index 0000000..5e3df0d --- /dev/null +++ b/programs/ido-program/migrations/deploy.ts @@ -0,0 +1,12 @@ +// Migrations are an early feature. Currently, they're nothing more than this +// single deploy script that's invoked from the CLI, injecting a provider +// configured from the workspace's Anchor.toml. + +const anchor = require("@project-serum/anchor"); + +module.exports = async function (provider) { + // Configure client to use the provider. + anchor.setProvider(provider); + + // Add your deploy script here. +}; diff --git a/programs/ido-program/package.json b/programs/ido-program/package.json new file mode 100644 index 0000000..bfab59d --- /dev/null +++ b/programs/ido-program/package.json @@ -0,0 +1,20 @@ +{ + "scripts": { + "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", + "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" + }, + "dependencies": { + "@project-serum/anchor": "^0.26.0", + "@solana/spl-token": "^0.3.8" + }, + "devDependencies": { + "@types/bn.js": "^5.1.0", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.0.0", + "chai": "^4.3.4", + "mocha": "^9.0.3", + "prettier": "^2.6.2", + "ts-mocha": "^10.0.0", + "typescript": "^4.3.5" + } +} diff --git a/programs/ido-program/programs/ido-program/Cargo.toml b/programs/ido-program/programs/ido-program/Cargo.toml new file mode 100644 index 0000000..0705db9 --- /dev/null +++ b/programs/ido-program/programs/ido-program/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ido-program" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "ido_program" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.28.0" +anchor-spl = "0.28.0" \ No newline at end of file diff --git a/programs/ido-program/programs/ido-program/Xargo.toml b/programs/ido-program/programs/ido-program/Xargo.toml new file mode 100644 index 0000000..475fb71 --- /dev/null +++ b/programs/ido-program/programs/ido-program/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs new file mode 100644 index 0000000..02053a6 --- /dev/null +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -0,0 +1,438 @@ +use anchor_lang::prelude::*; +use anchor_lang::solana_program::program_option::COption; +use anchor_spl::token::{self, Burn, Mint, MintTo, TokenAccount, Transfer, Token}; + +declare_id!("GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6"); + +#[program] +pub mod ido_program { + use super::*; + + #[access_control(InitializePool::accounts(&ctx, bump) pre_ido_phase(&ctx, start_ido_ts))] + pub fn initialize_pool( + ctx: Context, + total_native_tokens: u64, + start_ido_ts: i64, + end_ido_ts: i64, + withdraw_fiat_ts: i64, + bump: u8, + ) -> Result<()> { + + if !(start_ido_ts < end_ido_ts + && end_ido_ts < withdraw_fiat_ts) + { + return Err(ErrorCode::NonSequentialTimestamps.into()); + } + + if total_native_tokens == 0 { + return Err(ErrorCode::InvalidParameter.into()); + } + + let pool = &mut ctx.accounts.pool; + + pool.pool_authority = *ctx.accounts.authority.key; + pool.redeemable_mint = ctx.accounts.redeemable_mint.key(); + pool.native_mint = ctx.accounts.native_mint.key(); + pool.fiat_mint = ctx.accounts.fiat_mint.key(); + pool.pool_native = ctx.accounts.pool_native.key(); + pool.pool_fiat = ctx.accounts.pool_fiat.key(); + pool.total_native_tokens = total_native_tokens; + pool.start_ido_ts = start_ido_ts; + pool.end_ido_ts = end_ido_ts; + pool.withdraw_fiat_ts = withdraw_fiat_ts; + + pool.bump = bump; + + //Transfer Native tokens from Creator to Pool Account + let cpi_accounts = Transfer { + from: ctx.accounts.creator_native.to_account_info(), + to: ctx.accounts.pool_native.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + token::transfer(cpi_ctx, total_native_tokens)?; + + Ok(()) + } + + #[access_control(unrestricted_phase(&ctx))] + pub fn exchange_fiat_for_redeemable( + ctx: Context, + amount: u64, + ) -> Result<()> { + if amount == 0 { + return Err(ErrorCode::InvalidParameter.into()); + } + // While token::transfer will check this, we prefer a verbose error msg + if ctx.accounts.investor_fiat.amount < amount { + return Err(ErrorCode::LowFiat.into()); + } + + // Transfer investor's fiat to pool fiat account. + let cpi_accounts = Transfer { + from: ctx.accounts.investor_fiat.to_account_info(), + to: ctx.accounts.pool_fiat.to_account_info(), + authority: ctx.accounts.authority.to_account_info().clone(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + token::transfer(cpi_ctx, amount)?; + + // Mint Redeemable to investor Redeemable account. + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + let cpi_accounts = MintTo { + mint: ctx.accounts.redeemable_mint.to_account_info(), + to: ctx.accounts.investor_redeemable.to_account_info(), + authority: ctx.accounts.pool_signer.clone(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + token::mint_to(cpi_ctx, amount)?; + + Ok(()) + } + + #[access_control(ido_over(&ctx.accounts.pool, &ctx.accounts.clock))] + pub fn exchange_redeemable_for_native( + ctx: Context, + ) -> Result<()> { + let native_amount = (ctx.accounts.investor_redeemable.amount as u128) + .checked_mul(ctx.accounts.pool_native.amount as u128) + .unwrap() + .checked_div(ctx.accounts.redeemable_mint.supply as u128) + .unwrap(); + + let cpi_accounts = Burn { + mint: ctx.accounts.redeemable_mint.to_account_info(), + from: ctx.accounts.investor_redeemable.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + + token::burn(cpi_ctx, ctx.accounts.investor_redeemable.amount)?; + + + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + + let cpi_accounts = Transfer { + from: ctx.accounts.pool_native.to_account_info(), + to: ctx.accounts.investor_native.to_account_info(), + authority: ctx.accounts.pool_signer.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + + token::transfer(cpi_ctx, native_amount as u64)?; + + Ok(()) + } + + #[access_control(can_withdraw_fiat(&ctx.accounts.pool, &ctx.accounts.clock))] + pub fn withdraw_pool_fiat(ctx: Context) -> Result<()> { + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + let cpi_accounts = Transfer { + from: ctx.accounts.pool_fiat.to_account_info(), + to: ctx.accounts.creator_fiat.to_account_info(), + authority: ctx.accounts.pool_signer.to_account_info(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; + + Ok(()) + } +} + + + +#[derive(Accounts)] +pub struct InitializePool<'info> { + #[account(init, payer = authority, space = PoolAccount::LEN)] + pub pool: Box>, + + /// CHECK: This is not dangerous + #[account(mut)] + pub pool_signer: AccountInfo<'info>, + + #[account( + constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key), + constraint = redeemable_mint.supply == 0 + )] + pub redeemable_mint: Box>, + + #[account(constraint = fiat_mint.decimals == redeemable_mint.decimals)] + pub fiat_mint: Box>, + + #[account(constraint = pool_native.mint == *native_mint.to_account_info().key)] + pub native_mint: Box>, + + #[account(mut, constraint = pool_native.owner == *pool_signer.key)] + pub pool_native: Box>, + + #[account(constraint = pool_fiat.owner == *pool_signer.key)] + pub pool_fiat: Box>, + + #[account(mut)] + pub authority: Signer<'info>, + + #[account(mut)] + pub creator_native: Box>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub rent: Sysvar<'info, Rent>, + + pub clock: Sysvar<'info, Clock>, + + pub system_program: Program<'info, System>, +} + +impl<'info> InitializePool<'info> { + fn accounts(ctx: &Context>, bump: u8) -> Result<()> { + let expected_signer = Pubkey::create_program_address( + &[ctx.accounts.pool_native.mint.as_ref(), &[bump]], + ctx.program_id, + ) + .map_err(|_| ErrorCode::InvalidBump)?; + if ctx.accounts.pool_signer.key != &expected_signer { + return Err(ErrorCode::InvalidBump.into()); + } + Ok(()) + } +} + +#[derive(Accounts)] +pub struct ExchangeFiatForRedeemable<'info> { + #[account(mut, has_one = redeemable_mint, has_one = pool_fiat)] + pub pool: Box>, + + ///CHECK: This is not dangerous + #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] + pool_signer: AccountInfo<'info>, + + #[account( + mut, + constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + )] + pub redeemable_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + pub fiat_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] + pub pool_fiat: Account<'info, TokenAccount>, + + #[account(mut)] + pub authority: Signer<'info>, + + #[account(mut, constraint = investor_fiat.owner == *authority.key)] + pub investor_fiat: Account<'info, TokenAccount>, + + #[account(mut, constraint = investor_redeemable.owner == *authority.key)] + pub investor_redeemable: Account<'info, TokenAccount>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub clock: Sysvar<'info, Clock>, +} + +#[derive(Accounts)] +pub struct ExchangeRedeemableForNative<'info> { + #[account(has_one = redeemable_mint, has_one = pool_native)] + pub pool: Box>, + + /// CHECK: This is not dangerous + #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] + pool_signer: AccountInfo<'info>, + + #[account( + mut, + constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + )] + pub redeemable_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_native.owner == *pool_signer.key)] + pub pool_native: Account<'info, TokenAccount>, + + #[account(mut)] + pub authority: Signer<'info>, + + #[account(mut, constraint = investor_native.owner == *authority.key)] + pub investor_native: Account<'info, TokenAccount>, + + #[account(mut, constraint = investor_redeemable.owner == *authority.key)] + pub investor_redeemable: Account<'info, TokenAccount>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub clock: Sysvar<'info, Clock>, +} + +#[derive(Accounts)] +pub struct WithdrawPoolFiat<'info> { + #[account(has_one = pool_fiat)] + pub pool: Box>, + + ///CHECK: This is not dangerous + #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] + pub pool_signer: AccountInfo<'info>, + + #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + pub fiat_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] + pub pool_fiat: Account<'info, TokenAccount>, + + #[account(mut)] + pub payer: Signer<'info>, + + #[account(mut)] + pub creator_fiat: Account<'info, TokenAccount>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub clock: Sysvar<'info, Clock>, +} + +#[account] +pub struct PoolAccount { + /// Authority of the Pool + pub pool_authority: Pubkey, + + /// Mint of redeemable tokens (Intermediate tokens which will be exchanged for native tokens) + pub redeemable_mint: Pubkey, + + /// Mint of project tokens + pub native_mint: Pubkey, + + /// Mint of fiat tokens + pub fiat_mint: Pubkey, + + /// Token Account of Pool associated with the project token mint + pub pool_native: Pubkey, + + /// Token Account of Pool associated with fiat mint + pub pool_fiat: Pubkey, + + /// Total number of native tokens being distributed + pub total_native_tokens: u64, + + /// Unix timestamp for starting IDO + pub start_ido_ts: i64, + + /// Unix timestamp for ending IDO + pub end_ido_ts: i64, + + /// Unix timestamp for withdrawing Fiat from pool + pub withdraw_fiat_ts: i64, + + /// Bump + pub bump: u8, +} + + +impl PoolAccount { + pub const LEN: usize = DISCRIMINATOR_LENGTH // Discriminator Length + + PUBKEY_LENGTH // Pool Authority + + PUBKEY_LENGTH // Redeemable Mint + + PUBKEY_LENGTH // Fiat Mint + + PUBKEY_LENGTH // Pool Native Token Account + + PUBKEY_LENGTH // Native Mint + + PUBKEY_LENGTH // Pool fiat Token Account + + DATA_LENGTH_64 // Total Native Token Amount + + DATA_LENGTH_64 // Start IDO TS + + DATA_LENGTH_64 // End IDO TS + + DATA_LENGTH_64 // Withdraw Fiat TS + + DATA_LENGTH_8; // Bump +} + +const DISCRIMINATOR_LENGTH: usize = 8; +const PUBKEY_LENGTH: usize = 32; +const DATA_LENGTH_64: usize = 8; +const DATA_LENGTH_8: usize = 1; + +#[error_code] +pub enum ErrorCode { + #[msg("Timestamps are not Sequential")] + NonSequentialTimestamps, + #[msg("Invalid Parameter")] + InvalidParameter, + #[msg("Invalid Bump")] + InvalidBump, + #[msg("IDO has not begun yet")] + IdoFuture, + #[msg("Not the correct time to invest")] + WrongInvestingTime, + #[msg("Insufficient Fiat Tokens")] + LowFiat, + #[msg("IDO has not ended yet")] + IdoNotOver, + #[msg("Cannot withdraw Fiat yet")] + CannotWithdrawYet +} + + +// Access Control Modifiers + +// IDO Starts in the Future +fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) -> Result<()> { + if !(ctx.accounts.clock.unix_timestamp < start_ido_ts) { + return Err(ErrorCode::IdoFuture.into()); + } + Ok(()) +} + +// Unrestricted Phase +fn unrestricted_phase<'info>(ctx: &Context>) -> Result<()> { + if !( + ctx.accounts.pool.start_ido_ts < ctx.accounts.clock.unix_timestamp + && + ctx.accounts.pool.end_ido_ts > ctx.accounts.clock.unix_timestamp + ) { + return Err(ErrorCode::WrongInvestingTime.into()); + } + Ok(()) +} + +//iDO Over +fn ido_over<'info>( + pool_account: &Account<'info, PoolAccount>, + clock: &Sysvar<'info, Clock>, +) -> Result<()> { + if !(pool_account.end_ido_ts < clock.unix_timestamp) { + return Err(ErrorCode::IdoNotOver.into()); + } + Ok(()) +} + +//Can Withdraw fiat +fn can_withdraw_fiat<'info>( + pool_account: &Account<'info, PoolAccount>, + clock: &Sysvar<'info, Clock>, +) -> Result<()> { + if !(pool_account.withdraw_fiat_ts < clock.unix_timestamp) { + return Err(ErrorCode::CannotWithdrawYet.into()); + } + Ok(()) +} \ No newline at end of file diff --git a/programs/ido-program/tests/ido-program.ts b/programs/ido-program/tests/ido-program.ts new file mode 100644 index 0000000..3853f14 --- /dev/null +++ b/programs/ido-program/tests/ido-program.ts @@ -0,0 +1,466 @@ +import * as anchor from "@project-serum/anchor"; +import { Transaction, SystemProgram, PublicKey } from "@solana/web3.js"; +import { + createAccount, + createMint, + getAccount, + getOrCreateAssociatedTokenAccount, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { Program } from "@project-serum/anchor"; +import { assert } from "chai"; +import { IdoProgram } from "../target/types/ido_program"; + +describe("ido-program", () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.local(); + anchor.setProvider(provider); + + const program = anchor.workspace.IdoProgram as Program; + + let nativeTokenAmount = new anchor.BN(1000000); + + let fiatMint: PublicKey; + let redeemableMint: PublicKey; + let nativeMint: PublicKey; + + let projectFiat: PublicKey; + let projectNative: PublicKey; + + let investorFiat: PublicKey; + let investorNative: PublicKey; + let investorRedeemable: PublicKey; + + let poolNative: PublicKey; + let poolFiat: PublicKey; + + let poolSigner: PublicKey; + + let nowBn: anchor.BN; + let startIdoTs: anchor.BN; + let endIdoTs: anchor.BN; + let withDrawFiatTs: anchor.BN; + + const payer = anchor.web3.Keypair.generate(); + const mintAuthority = anchor.web3.Keypair.generate(); + + const project = anchor.web3.Keypair.generate(); + const investor = anchor.web3.Keypair.generate(); + + let pool = anchor.web3.Keypair.generate(); + + it("Can initialize the program state", async () => { + const transferSig = await provider.connection.requestAirdrop( + payer.publicKey, + 10000000000 + ); + + const latestBlockHash = await provider.connection.getLatestBlockhash(); + + await provider.connection.confirmTransaction({ + blockhash: latestBlockHash.blockhash, + lastValidBlockHeight: latestBlockHash.lastValidBlockHeight, + signature: transferSig, + }); + + const tx = new Transaction(); + + tx.add( + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: mintAuthority.publicKey, + lamports: 2000000000, + }), + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: project.publicKey, + lamports: 2000000000, + }), + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: investor.publicKey, + lamports: 2000000000, + }) + ); + + await provider.sendAndConfirm(tx, [payer]); + + fiatMint = await createMint( + provider.connection, + payer, + mintAuthority.publicKey, + undefined, + 0, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + nativeMint = await createMint( + provider.connection, + payer, + mintAuthority.publicKey, + undefined, + 0, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + projectFiat = await createAccount( + provider.connection, + payer, + fiatMint, + project.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + projectNative = await createAccount( + provider.connection, + payer, + nativeMint, + project.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + investorFiat = await createAccount( + provider.connection, + payer, + fiatMint, + investor.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + investorNative = await createAccount( + provider.connection, + payer, + nativeMint, + investor.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + payer, + nativeMint, + projectNative, + mintAuthority, + nativeTokenAmount.toNumber() + ); + + await mintTo( + provider.connection, + payer, + fiatMint, + investorFiat, + mintAuthority, + 10000 + ); + + const projectNativeTokenAccount = await getAccount( + provider.connection, + projectNative + ); + + assert.strictEqual( + projectNativeTokenAccount.amount.toString(), + nativeTokenAmount.toNumber().toString() + ); + }); + + it("Can initialize the Pool", async () => { + const [_poolSigner, bump] = + anchor.web3.PublicKey.findProgramAddressSync( + [nativeMint.toBuffer()], + program.programId + ); + + poolSigner = _poolSigner; + + redeemableMint = await createMint( + provider.connection, + payer, + poolSigner, + undefined, + 0, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + investorRedeemable = await createAccount( + provider.connection, + payer, + redeemableMint, + investor.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + let poolNativeAccount = await getOrCreateAssociatedTokenAccount( + provider.connection, + payer, + nativeMint, + poolSigner, + true, + undefined, + undefined, + TOKEN_PROGRAM_ID, + undefined + ); + + poolNative = poolNativeAccount.address; + + let poolFiatAccount = await getOrCreateAssociatedTokenAccount( + provider.connection, + payer, + fiatMint, + poolSigner, + true, + undefined, + undefined, + TOKEN_PROGRAM_ID, + undefined + ); + + poolFiat = poolFiatAccount.address; + + nowBn = new anchor.BN(Date.now() / 1000); + startIdoTs = nowBn.add(new anchor.BN(10)); + endIdoTs = nowBn.add(new anchor.BN(20)); + withDrawFiatTs = nowBn.add(new anchor.BN(30)); + + await program.methods + .initializePool( + nativeTokenAmount, + startIdoTs, + endIdoTs, + withDrawFiatTs, + bump + ) + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + redeemableMint: redeemableMint, + fiatMint: fiatMint, + nativeMint: nativeMint, + poolNative: poolNative, + poolFiat: poolFiat, + authority: project.publicKey, + creatorNative: projectNative, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + }) + .signers([pool, project]) + .rpc(); + + const poolNativeTokenAccount = await getAccount( + provider.connection, + poolNative + ); + + assert.strictEqual( + poolNativeTokenAccount.amount.toString(), + nativeTokenAmount.toNumber().toString() + ); + + const createdPool = await program.account.poolAccount.fetch( + pool.publicKey + ); + + assert.strictEqual( + createdPool.poolAuthority.toBase58(), + project.publicKey.toBase58() + ); + assert.strictEqual( + createdPool.redeemableMint.toBase58(), + redeemableMint.toBase58() + ); + assert.strictEqual( + createdPool.poolNative.toBase58(), + poolNative.toBase58() + ); + assert.strictEqual( + createdPool.nativeMint.toBase58(), + nativeMint.toBase58() + ); + assert.strictEqual( + createdPool.poolFiat.toBase58(), + poolFiat.toBase58() + ); + assert.strictEqual( + createdPool.totalNativeTokens.toNumber().toString(), + nativeTokenAmount.toString() + ); + assert.strictEqual( + createdPool.startIdoTs.toNumber().toString(), + startIdoTs.toString() + ); + assert.strictEqual( + createdPool.endIdoTs.toNumber().toString(), + endIdoTs.toString() + ); + assert.strictEqual( + createdPool.withdrawFiatTs.toNumber().toString(), + withDrawFiatTs.toString() + ); + }); + + let firstDeposit = 5000; + + it("Can exchange investor Fiat for redeemable tokens", async () => { + if (Date.now() < startIdoTs.toNumber() * 1000) { + await sleep(startIdoTs.toNumber() * 1000 - Date.now() + 5000); + } + + await program.methods + .exchangeFiatForRedeemable(new anchor.BN(firstDeposit)) + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + redeemableMint: redeemableMint, + fiatMint: fiatMint, + poolFiat: poolFiat, + authority: investor.publicKey, + investorFiat: investorFiat, + investorRedeemable: investorRedeemable, + tokenProgram: TOKEN_PROGRAM_ID, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + }) + .signers([investor]) + .rpc(); + + const poolFiatTokenAccount = await getAccount( + provider.connection, + poolFiat + ); + + assert.strictEqual( + poolFiatTokenAccount.amount.toString(), + firstDeposit.toString() + ); + + const investorFiatTokenAccount = await getAccount( + provider.connection, + investorFiat + ); + + const investorRedeemableTokenAccount = await getAccount( + provider.connection, + investorRedeemable + ); + + assert.strictEqual( + investorFiatTokenAccount.amount.toString(), + (10000 - firstDeposit).toString() + ); + + assert.strictEqual( + investorRedeemableTokenAccount.amount.toString(), + firstDeposit.toString() + ); + }); + + it("Can exchange investor Redeemable tokens for Native tokens", async () => { + if (Date.now() < endIdoTs.toNumber() * 1000) { + await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 5000); + } + + await program.methods + .exchangeRedeemableForNative() + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + redeemableMint: redeemableMint, + poolNative: poolNative, + authority: investor.publicKey, + investorNative: investorNative, + investorRedeemable: investorRedeemable, + tokenProgram: TOKEN_PROGRAM_ID, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + }) + .signers([investor]) + .rpc(); + + const poolNativeTokenAccount = await getAccount( + provider.connection, + poolNative + ); + + assert.strictEqual(poolNativeTokenAccount.amount.toString(), "0"); + + const investorNativeTokenAccount = await getAccount( + provider.connection, + investorNative + ); + + const investorRedeemableTokenAccount = await getAccount( + provider.connection, + investorRedeemable + ); + + assert.strictEqual( + investorNativeTokenAccount.amount.toString(), + nativeTokenAmount.toString() + ); + + assert.strictEqual( + investorRedeemableTokenAccount.amount.toString(), + "0" + ); + }); + + it("Can withdraw total Fiat from pool account", async () => { + if (Date.now() < withDrawFiatTs.toNumber() * 1000) { + await sleep(withDrawFiatTs.toNumber() * 1000 - Date.now() + 5000); + } + + await program.methods + .withdrawPoolFiat() + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + fiatMint: fiatMint, + poolFiat: poolFiat, + payer: project.publicKey, + creatorFiat: projectFiat, + tokenProgram: TOKEN_PROGRAM_ID, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + }) + .signers([project]) + .rpc(); + + const poolFiatTokenAccount = await getAccount( + provider.connection, + poolFiat + ); + + assert.strictEqual(poolFiatTokenAccount.amount.toString(), "0"); + + const projectFiatTokenAccount = await getAccount( + provider.connection, + projectFiat + ); + + assert.strictEqual( + projectFiatTokenAccount.amount.toString(), + firstDeposit.toString() + ); + }); +}); + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/programs/ido-program/tsconfig.json b/programs/ido-program/tsconfig.json new file mode 100644 index 0000000..558b83e --- /dev/null +++ b/programs/ido-program/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } + } + \ No newline at end of file diff --git a/programs/ido-program/yarn.lock b/programs/ido-program/yarn.lock new file mode 100644 index 0000000..7c7a9a3 --- /dev/null +++ b/programs/ido-program/yarn.lock @@ -0,0 +1,1188 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2": + "integrity" "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==" + "resolved" "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz" + "version" "7.22.5" + dependencies: + "regenerator-runtime" "^0.13.11" + +"@coral-xyz/borsh@^0.26.0": + "integrity" "sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==" + "resolved" "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.26.0.tgz" + "version" "0.26.0" + dependencies: + "bn.js" "^5.1.2" + "buffer-layout" "^1.2.0" + +"@noble/curves@^1.0.0": + "integrity" "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==" + "resolved" "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" + "version" "1.1.0" + dependencies: + "@noble/hashes" "1.3.1" + +"@noble/hashes@^1.3.0", "@noble/hashes@1.3.1": + "integrity" "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + "resolved" "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" + "version" "1.3.1" + +"@project-serum/anchor@^0.26.0": + "integrity" "sha512-Nq+COIjE1135T7qfnOHEn7E0q39bQTgXLFk837/rgFe6Hkew9WML7eHsS+lSYD2p3OJaTiUOHTAq1lHy36oIqQ==" + "resolved" "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.26.0.tgz" + "version" "0.26.0" + dependencies: + "@coral-xyz/borsh" "^0.26.0" + "@solana/web3.js" "^1.68.0" + "base64-js" "^1.5.1" + "bn.js" "^5.1.2" + "bs58" "^4.0.1" + "buffer-layout" "^1.2.2" + "camelcase" "^6.3.0" + "cross-fetch" "^3.1.5" + "crypto-hash" "^1.3.0" + "eventemitter3" "^4.0.7" + "js-sha256" "^0.9.0" + "pako" "^2.0.3" + "snake-case" "^3.0.4" + "superstruct" "^0.15.4" + "toml" "^3.0.0" + +"@solana/buffer-layout-utils@^0.2.0": + "integrity" "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==" + "resolved" "https://registry.npmjs.org/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz" + "version" "0.2.0" + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/web3.js" "^1.32.0" + "bigint-buffer" "^1.1.5" + "bignumber.js" "^9.0.1" + +"@solana/buffer-layout@^4.0.0": + "integrity" "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==" + "resolved" "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "buffer" "~6.0.3" + +"@solana/spl-token@^0.3.8": + "integrity" "sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg==" + "resolved" "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.8.tgz" + "version" "0.3.8" + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "buffer" "^6.0.3" + +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.47.4", "@solana/web3.js@^1.68.0": + "integrity" "sha512-PHaO0BdoiQRPpieC1p31wJsBaxwIOWLh8j2ocXNKX8boCQVldt26Jqm2tZE4KlrvnCIV78owPLv1pEUgqhxZ3w==" + "resolved" "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.77.3.tgz" + "version" "1.77.3" + dependencies: + "@babel/runtime" "^7.12.5" + "@noble/curves" "^1.0.0" + "@noble/hashes" "^1.3.0" + "@solana/buffer-layout" "^4.0.0" + "agentkeepalive" "^4.2.1" + "bigint-buffer" "^1.1.5" + "bn.js" "^5.0.0" + "borsh" "^0.7.0" + "bs58" "^4.0.1" + "buffer" "6.0.3" + "fast-stable-stringify" "^1.0.0" + "jayson" "^4.1.0" + "node-fetch" "^2.6.7" + "rpc-websockets" "^7.5.1" + "superstruct" "^0.14.2" + +"@types/bn.js@^5.1.0": + "integrity" "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==" + "resolved" "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz" + "version" "5.1.1" + dependencies: + "@types/node" "*" + +"@types/chai@^4.3.0": + "integrity" "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==" + "resolved" "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz" + "version" "4.3.5" + +"@types/connect@^3.4.33": + "integrity" "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==" + "resolved" "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + "version" "3.4.35" + dependencies: + "@types/node" "*" + +"@types/json5@^0.0.29": + "integrity" "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + "resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + "version" "0.0.29" + +"@types/mocha@^9.0.0": + "integrity" "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==" + "resolved" "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz" + "version" "9.1.1" + +"@types/node@*": + "integrity" "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-20.3.0.tgz" + "version" "20.3.0" + +"@types/node@^12.12.54": + "integrity" "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" + "resolved" "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz" + "version" "12.20.55" + +"@types/ws@^7.4.4": + "integrity" "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==" + "resolved" "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz" + "version" "7.4.7" + dependencies: + "@types/node" "*" + +"@ungap/promise-all-settled@1.1.2": + "integrity" "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==" + "resolved" "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" + "version" "1.1.2" + +"agentkeepalive@^4.2.1": + "integrity" "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==" + "resolved" "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "debug" "^4.1.0" + "depd" "^2.0.0" + "humanize-ms" "^1.2.1" + +"ansi-colors@4.1.1": + "integrity" "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" + "resolved" "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + "version" "4.1.1" + +"ansi-regex@^5.0.1": + "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "resolved" "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + "version" "5.0.1" + +"ansi-styles@^4.0.0", "ansi-styles@^4.1.0": + "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==" + "resolved" "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + "version" "4.3.0" + dependencies: + "color-convert" "^2.0.1" + +"anymatch@~3.1.2": + "integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==" + "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + "version" "3.1.3" + dependencies: + "normalize-path" "^3.0.0" + "picomatch" "^2.0.4" + +"argparse@^2.0.1": + "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "resolved" "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + "version" "2.0.1" + +"arrify@^1.0.0": + "integrity" "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==" + "resolved" "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + "version" "1.0.1" + +"assertion-error@^1.1.0": + "integrity" "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + "resolved" "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz" + "version" "1.1.0" + +"balanced-match@^1.0.0": + "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "resolved" "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + "version" "1.0.2" + +"base-x@^3.0.2": + "integrity" "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==" + "resolved" "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz" + "version" "3.0.9" + dependencies: + "safe-buffer" "^5.0.1" + +"base64-js@^1.3.1", "base64-js@^1.5.1": + "integrity" "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "resolved" "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + "version" "1.5.1" + +"bigint-buffer@^1.1.5": + "integrity" "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==" + "resolved" "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz" + "version" "1.1.5" + dependencies: + "bindings" "^1.3.0" + +"bignumber.js@^9.0.1": + "integrity" "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==" + "resolved" "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz" + "version" "9.1.1" + +"binary-extensions@^2.0.0": + "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + "version" "2.2.0" + +"bindings@^1.3.0": + "integrity" "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==" + "resolved" "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" + "version" "1.5.0" + dependencies: + "file-uri-to-path" "1.0.0" + +"bn.js@^5.0.0", "bn.js@^5.1.2", "bn.js@^5.2.0": + "integrity" "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + "resolved" "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" + "version" "5.2.1" + +"borsh@^0.7.0": + "integrity" "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==" + "resolved" "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz" + "version" "0.7.0" + dependencies: + "bn.js" "^5.2.0" + "bs58" "^4.0.0" + "text-encoding-utf-8" "^1.0.2" + +"brace-expansion@^1.1.7": + "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==" + "resolved" "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + "version" "1.1.11" + dependencies: + "balanced-match" "^1.0.0" + "concat-map" "0.0.1" + +"braces@~3.0.2": + "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==" + "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + "version" "3.0.2" + dependencies: + "fill-range" "^7.0.1" + +"browser-stdout@1.3.1": + "integrity" "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + "resolved" "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" + "version" "1.3.1" + +"bs58@^4.0.0", "bs58@^4.0.1": + "integrity" "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==" + "resolved" "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz" + "version" "4.0.1" + dependencies: + "base-x" "^3.0.2" + +"buffer-from@^1.0.0", "buffer-from@^1.1.0": + "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + "version" "1.1.2" + +"buffer-layout@^1.2.0", "buffer-layout@^1.2.2": + "integrity" "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==" + "resolved" "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" + "version" "1.2.2" + +"buffer@^6.0.3", "buffer@~6.0.3", "buffer@6.0.3": + "integrity" "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==" + "resolved" "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + "version" "6.0.3" + dependencies: + "base64-js" "^1.3.1" + "ieee754" "^1.2.1" + +"bufferutil@^4.0.1": + "integrity" "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==" + "resolved" "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz" + "version" "4.0.7" + dependencies: + "node-gyp-build" "^4.3.0" + +"camelcase@^6.0.0", "camelcase@^6.3.0": + "integrity" "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + "resolved" "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + "version" "6.3.0" + +"chai@^4.3.4": + "integrity" "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==" + "resolved" "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz" + "version" "4.3.7" + dependencies: + "assertion-error" "^1.1.0" + "check-error" "^1.0.2" + "deep-eql" "^4.1.2" + "get-func-name" "^2.0.0" + "loupe" "^2.3.1" + "pathval" "^1.1.1" + "type-detect" "^4.0.5" + +"chalk@^4.1.0": + "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==" + "resolved" "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + "version" "4.1.2" + dependencies: + "ansi-styles" "^4.1.0" + "supports-color" "^7.1.0" + +"check-error@^1.0.2": + "integrity" "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==" + "resolved" "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz" + "version" "1.0.2" + +"chokidar@3.5.3": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" + dependencies: + "anymatch" "~3.1.2" + "braces" "~3.0.2" + "glob-parent" "~5.1.2" + "is-binary-path" "~2.1.0" + "is-glob" "~4.0.1" + "normalize-path" "~3.0.0" + "readdirp" "~3.6.0" + optionalDependencies: + "fsevents" "~2.3.2" + +"cliui@^7.0.2": + "integrity" "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==" + "resolved" "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + "version" "7.0.4" + dependencies: + "string-width" "^4.2.0" + "strip-ansi" "^6.0.0" + "wrap-ansi" "^7.0.0" + +"color-convert@^2.0.1": + "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==" + "resolved" "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + "version" "2.0.1" + dependencies: + "color-name" "~1.1.4" + +"color-name@~1.1.4": + "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "resolved" "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + "version" "1.1.4" + +"commander@^2.20.3": + "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "resolved" "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + "version" "2.20.3" + +"concat-map@0.0.1": + "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "resolved" "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + "version" "0.0.1" + +"cross-fetch@^3.1.5": + "integrity" "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==" + "resolved" "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz" + "version" "3.1.6" + dependencies: + "node-fetch" "^2.6.11" + +"crypto-hash@^1.3.0": + "integrity" "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==" + "resolved" "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz" + "version" "1.3.0" + +"debug@^4.1.0": + "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + "version" "4.3.4" + dependencies: + "ms" "2.1.2" + +"debug@4.3.3": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" + dependencies: + "ms" "2.1.2" + +"decamelize@^4.0.0": + "integrity" "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" + "resolved" "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" + "version" "4.0.0" + +"deep-eql@^4.1.2": + "integrity" "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==" + "resolved" "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz" + "version" "4.1.3" + dependencies: + "type-detect" "^4.0.0" + +"delay@^5.0.0": + "integrity" "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" + "resolved" "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" + "version" "5.0.0" + +"depd@^2.0.0": + "integrity" "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "resolved" "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + "version" "2.0.0" + +"diff@^3.1.0": + "integrity" "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + "resolved" "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" + "version" "3.5.0" + +"diff@5.0.0": + "integrity" "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" + "resolved" "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" + "version" "5.0.0" + +"dot-case@^3.0.4": + "integrity" "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==" + "resolved" "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "no-case" "^3.0.4" + "tslib" "^2.0.3" + +"emoji-regex@^8.0.0": + "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "resolved" "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + "version" "8.0.0" + +"es6-promise@^4.0.3": + "integrity" "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + "resolved" "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" + "version" "4.2.8" + +"es6-promisify@^5.0.0": + "integrity" "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==" + "resolved" "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "es6-promise" "^4.0.3" + +"escalade@^3.1.1": + "integrity" "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "resolved" "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + "version" "3.1.1" + +"escape-string-regexp@4.0.0": + "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + "resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + "version" "4.0.0" + +"eventemitter3@^4.0.7": + "integrity" "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + "resolved" "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + "version" "4.0.7" + +"eyes@^0.1.8": + "integrity" "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==" + "resolved" "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" + "version" "0.1.8" + +"fast-stable-stringify@^1.0.0": + "integrity" "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==" + "resolved" "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz" + "version" "1.0.0" + +"file-uri-to-path@1.0.0": + "integrity" "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "resolved" "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" + "version" "1.0.0" + +"fill-range@^7.0.1": + "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==" + "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "to-regex-range" "^5.0.1" + +"find-up@5.0.0": + "integrity" "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==" + "resolved" "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "locate-path" "^6.0.0" + "path-exists" "^4.0.0" + +"flat@^5.0.2": + "integrity" "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" + "resolved" "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + "version" "5.0.2" + +"fs.realpath@^1.0.0": + "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "resolved" "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + "version" "1.0.0" + +"fsevents@~2.3.2": + "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==" + "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + "version" "2.3.2" + +"get-caller-file@^2.0.5": + "integrity" "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "resolved" "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + "version" "2.0.5" + +"get-func-name@^2.0.0": + "integrity" "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==" + "resolved" "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz" + "version" "2.0.0" + +"glob-parent@~5.1.2": + "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==" + "resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + "version" "5.1.2" + dependencies: + "is-glob" "^4.0.1" + +"glob@7.2.0": + "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "fs.realpath" "^1.0.0" + "inflight" "^1.0.4" + "inherits" "2" + "minimatch" "^3.0.4" + "once" "^1.3.0" + "path-is-absolute" "^1.0.0" + +"growl@1.10.5": + "integrity" "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + "resolved" "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" + "version" "1.10.5" + +"has-flag@^4.0.0": + "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "resolved" "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + "version" "4.0.0" + +"he@1.2.0": + "integrity" "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + "resolved" "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + "version" "1.2.0" + +"humanize-ms@^1.2.1": + "integrity" "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==" + "resolved" "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" + "version" "1.2.1" + dependencies: + "ms" "^2.0.0" + +"ieee754@^1.2.1": + "integrity" "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + "resolved" "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + "version" "1.2.1" + +"inflight@^1.0.4": + "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==" + "resolved" "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + "version" "1.0.6" + dependencies: + "once" "^1.3.0" + "wrappy" "1" + +"inherits@2": + "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + "version" "2.0.4" + +"is-binary-path@~2.1.0": + "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==" + "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "binary-extensions" "^2.0.0" + +"is-extglob@^2.1.1": + "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + "version" "2.1.1" + +"is-fullwidth-code-point@^3.0.0": + "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "resolved" "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + "version" "3.0.0" + +"is-glob@^4.0.1", "is-glob@~4.0.1": + "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==" + "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + "version" "4.0.3" + dependencies: + "is-extglob" "^2.1.1" + +"is-number@^7.0.0": + "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + "version" "7.0.0" + +"is-plain-obj@^2.1.0": + "integrity" "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==" + "resolved" "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + "version" "2.1.0" + +"is-unicode-supported@^0.1.0": + "integrity" "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" + "resolved" "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + "version" "0.1.0" + +"isexe@^2.0.0": + "integrity" "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "resolved" "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + "version" "2.0.0" + +"isomorphic-ws@^4.0.1": + "integrity" "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" + "resolved" "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz" + "version" "4.0.1" + +"jayson@^4.1.0": + "integrity" "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==" + "resolved" "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + "commander" "^2.20.3" + "delay" "^5.0.0" + "es6-promisify" "^5.0.0" + "eyes" "^0.1.8" + "isomorphic-ws" "^4.0.1" + "json-stringify-safe" "^5.0.1" + "JSONStream" "^1.3.5" + "uuid" "^8.3.2" + "ws" "^7.4.5" + +"js-sha256@^0.9.0": + "integrity" "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + "resolved" "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" + "version" "0.9.0" + +"js-yaml@4.1.0": + "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==" + "resolved" "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "argparse" "^2.0.1" + +"json-stringify-safe@^5.0.1": + "integrity" "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + "resolved" "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" + "version" "5.0.1" + +"json5@^1.0.2": + "integrity" "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==" + "resolved" "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + "version" "1.0.2" + dependencies: + "minimist" "^1.2.0" + +"jsonparse@^1.2.0": + "integrity" "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==" + "resolved" "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" + "version" "1.3.1" + +"JSONStream@^1.3.5": + "integrity" "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==" + "resolved" "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + "version" "1.3.5" + dependencies: + "jsonparse" "^1.2.0" + "through" ">=2.2.7 <3" + +"locate-path@^6.0.0": + "integrity" "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==" + "resolved" "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "p-locate" "^5.0.0" + +"log-symbols@4.1.0": + "integrity" "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==" + "resolved" "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + "version" "4.1.0" + dependencies: + "chalk" "^4.1.0" + "is-unicode-supported" "^0.1.0" + +"loupe@^2.3.1": + "integrity" "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==" + "resolved" "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz" + "version" "2.3.6" + dependencies: + "get-func-name" "^2.0.0" + +"lower-case@^2.0.2": + "integrity" "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==" + "resolved" "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "tslib" "^2.0.3" + +"make-error@^1.1.1": + "integrity" "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + "resolved" "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + "version" "1.3.6" + +"minimatch@^3.0.4": + "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + "version" "3.1.2" + dependencies: + "brace-expansion" "^1.1.7" + +"minimatch@4.2.1": + "integrity" "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" + "version" "4.2.1" + dependencies: + "brace-expansion" "^1.1.7" + +"minimist@^1.2.0", "minimist@^1.2.6": + "integrity" "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + "version" "1.2.8" + +"mkdirp@^0.5.1": + "integrity" "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==" + "resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + "version" "0.5.6" + dependencies: + "minimist" "^1.2.6" + +"mocha@^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X", "mocha@^9.0.3": + "integrity" "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==" + "resolved" "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz" + "version" "9.2.2" + dependencies: + "@ungap/promise-all-settled" "1.1.2" + "ansi-colors" "4.1.1" + "browser-stdout" "1.3.1" + "chokidar" "3.5.3" + "debug" "4.3.3" + "diff" "5.0.0" + "escape-string-regexp" "4.0.0" + "find-up" "5.0.0" + "glob" "7.2.0" + "growl" "1.10.5" + "he" "1.2.0" + "js-yaml" "4.1.0" + "log-symbols" "4.1.0" + "minimatch" "4.2.1" + "ms" "2.1.3" + "nanoid" "3.3.1" + "serialize-javascript" "6.0.0" + "strip-json-comments" "3.1.1" + "supports-color" "8.1.1" + "which" "2.0.2" + "workerpool" "6.2.0" + "yargs" "16.2.0" + "yargs-parser" "20.2.4" + "yargs-unparser" "2.0.0" + +"ms@^2.0.0": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" + +"ms@2.1.2": + "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + "version" "2.1.2" + +"ms@2.1.3": + "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + "version" "2.1.3" + +"nanoid@3.3.1": + "integrity" "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" + "version" "3.3.1" + +"no-case@^3.0.4": + "integrity" "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==" + "resolved" "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "lower-case" "^2.0.2" + "tslib" "^2.0.3" + +"node-fetch@^2.6.11", "node-fetch@^2.6.7": + "integrity" "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==" + "resolved" "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz" + "version" "2.6.11" + dependencies: + "whatwg-url" "^5.0.0" + +"node-gyp-build@^4.3.0": + "integrity" "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" + "resolved" "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz" + "version" "4.6.0" + +"normalize-path@^3.0.0", "normalize-path@~3.0.0": + "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + "version" "3.0.0" + +"once@^1.3.0": + "integrity" "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==" + "resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + "version" "1.4.0" + dependencies: + "wrappy" "1" + +"p-limit@^3.0.2": + "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==" + "resolved" "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + "version" "3.1.0" + dependencies: + "yocto-queue" "^0.1.0" + +"p-locate@^5.0.0": + "integrity" "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==" + "resolved" "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "p-limit" "^3.0.2" + +"pako@^2.0.3": + "integrity" "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + "resolved" "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" + "version" "2.1.0" + +"path-exists@^4.0.0": + "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "resolved" "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + "version" "4.0.0" + +"path-is-absolute@^1.0.0": + "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "resolved" "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + "version" "1.0.1" + +"pathval@^1.1.1": + "integrity" "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" + "resolved" "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" + "version" "1.1.1" + +"picomatch@^2.0.4", "picomatch@^2.2.1": + "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + "version" "2.3.1" + +"prettier@^2.6.2": + "integrity" "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" + "resolved" "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" + "version" "2.8.8" + +"randombytes@^2.1.0": + "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==" + "resolved" "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + "version" "2.1.0" + dependencies: + "safe-buffer" "^5.1.0" + +"readdirp@~3.6.0": + "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==" + "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + "version" "3.6.0" + dependencies: + "picomatch" "^2.2.1" + +"regenerator-runtime@^0.13.11": + "integrity" "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + "version" "0.13.11" + +"require-directory@^2.1.1": + "integrity" "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + "resolved" "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + "version" "2.1.1" + +"rpc-websockets@^7.5.1": + "integrity" "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==" + "resolved" "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.5.1.tgz" + "version" "7.5.1" + dependencies: + "@babel/runtime" "^7.17.2" + "eventemitter3" "^4.0.7" + "uuid" "^8.3.2" + "ws" "^8.5.0" + optionalDependencies: + "bufferutil" "^4.0.1" + "utf-8-validate" "^5.0.2" + +"safe-buffer@^5.0.1", "safe-buffer@^5.1.0": + "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + "version" "5.2.1" + +"serialize-javascript@6.0.0": + "integrity" "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==" + "resolved" "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + "version" "6.0.0" + dependencies: + "randombytes" "^2.1.0" + +"snake-case@^3.0.4": + "integrity" "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==" + "resolved" "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" + "version" "3.0.4" + dependencies: + "dot-case" "^3.0.4" + "tslib" "^2.0.3" + +"source-map-support@^0.5.6": + "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==" + "resolved" "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + "version" "0.5.21" + dependencies: + "buffer-from" "^1.0.0" + "source-map" "^0.6.0" + +"source-map@^0.6.0": + "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + "version" "0.6.1" + +"string-width@^4.1.0", "string-width@^4.2.0": + "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==" + "resolved" "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + "version" "4.2.3" + dependencies: + "emoji-regex" "^8.0.0" + "is-fullwidth-code-point" "^3.0.0" + "strip-ansi" "^6.0.1" + +"strip-ansi@^6.0.0", "strip-ansi@^6.0.1": + "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==" + "resolved" "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + "version" "6.0.1" + dependencies: + "ansi-regex" "^5.0.1" + +"strip-bom@^3.0.0": + "integrity" "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" + "resolved" "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + "version" "3.0.0" + +"strip-json-comments@3.1.1": + "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "resolved" "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + "version" "3.1.1" + +"superstruct@^0.14.2": + "integrity" "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==" + "resolved" "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz" + "version" "0.14.2" + +"superstruct@^0.15.4": + "integrity" "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" + "resolved" "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz" + "version" "0.15.5" + +"supports-color@^7.1.0": + "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + "version" "7.2.0" + dependencies: + "has-flag" "^4.0.0" + +"supports-color@8.1.1": + "integrity" "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==" + "resolved" "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + "version" "8.1.1" + dependencies: + "has-flag" "^4.0.0" + +"text-encoding-utf-8@^1.0.2": + "integrity" "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==" + "resolved" "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz" + "version" "1.0.2" + +"through@>=2.2.7 <3": + "integrity" "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + "resolved" "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + "version" "2.3.8" + +"to-regex-range@^5.0.1": + "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==" + "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + "version" "5.0.1" + dependencies: + "is-number" "^7.0.0" + +"toml@^3.0.0": + "integrity" "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + "resolved" "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" + "version" "3.0.0" + +"tr46@~0.0.3": + "integrity" "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "resolved" "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + "version" "0.0.3" + +"ts-mocha@^10.0.0": + "integrity" "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==" + "resolved" "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz" + "version" "10.0.0" + dependencies: + "ts-node" "7.0.1" + optionalDependencies: + "tsconfig-paths" "^3.5.0" + +"ts-node@7.0.1": + "integrity" "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==" + "resolved" "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz" + "version" "7.0.1" + dependencies: + "arrify" "^1.0.0" + "buffer-from" "^1.1.0" + "diff" "^3.1.0" + "make-error" "^1.1.1" + "minimist" "^1.2.0" + "mkdirp" "^0.5.1" + "source-map-support" "^0.5.6" + "yn" "^2.0.0" + +"tsconfig-paths@^3.5.0": + "integrity" "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==" + "resolved" "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz" + "version" "3.14.2" + dependencies: + "@types/json5" "^0.0.29" + "json5" "^1.0.2" + "minimist" "^1.2.6" + "strip-bom" "^3.0.0" + +"tslib@^2.0.3": + "integrity" "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + "resolved" "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz" + "version" "2.5.3" + +"type-detect@^4.0.0", "type-detect@^4.0.5": + "integrity" "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + "resolved" "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + "version" "4.0.8" + +"typescript@^4.3.5": + "integrity" "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" + "resolved" "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + "version" "4.9.5" + +"utf-8-validate@^5.0.2", "utf-8-validate@>=5.0.2": + "integrity" "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==" + "resolved" "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" + "version" "5.0.10" + dependencies: + "node-gyp-build" "^4.3.0" + +"uuid@^8.3.2": + "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + "version" "8.3.2" + +"webidl-conversions@^3.0.0": + "integrity" "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + "version" "3.0.1" + +"whatwg-url@^5.0.0": + "integrity" "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==" + "resolved" "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + "version" "5.0.0" + dependencies: + "tr46" "~0.0.3" + "webidl-conversions" "^3.0.0" + +"which@2.0.2": + "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==" + "resolved" "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + "version" "2.0.2" + dependencies: + "isexe" "^2.0.0" + +"workerpool@6.2.0": + "integrity" "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" + "resolved" "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" + "version" "6.2.0" + +"wrap-ansi@^7.0.0": + "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==" + "resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + "version" "7.0.0" + dependencies: + "ansi-styles" "^4.0.0" + "string-width" "^4.1.0" + "strip-ansi" "^6.0.0" + +"wrappy@1": + "integrity" "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + "version" "1.0.2" + +"ws@*", "ws@^7.4.5": + "integrity" "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" + "resolved" "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" + "version" "7.5.9" + +"ws@^8.5.0": + "integrity" "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==" + "resolved" "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz" + "version" "8.13.0" + +"y18n@^5.0.5": + "integrity" "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + "resolved" "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + "version" "5.0.8" + +"yargs-parser@^20.2.2": + "integrity" "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + "version" "20.2.9" + +"yargs-parser@20.2.4": + "integrity" "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" + "version" "20.2.4" + +"yargs-unparser@2.0.0": + "integrity" "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==" + "resolved" "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" + "version" "2.0.0" + dependencies: + "camelcase" "^6.0.0" + "decamelize" "^4.0.0" + "flat" "^5.0.2" + "is-plain-obj" "^2.1.0" + +"yargs@16.2.0": + "integrity" "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==" + "resolved" "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + "version" "16.2.0" + dependencies: + "cliui" "^7.0.2" + "escalade" "^3.1.1" + "get-caller-file" "^2.0.5" + "require-directory" "^2.1.1" + "string-width" "^4.2.0" + "y18n" "^5.0.5" + "yargs-parser" "^20.2.2" + +"yn@^2.0.0": + "integrity" "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==" + "resolved" "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz" + "version" "2.0.0" + +"yocto-queue@^0.1.0": + "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + "resolved" "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + "version" "0.1.0" diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ac6cb06..b75d2b9 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -24,7 +24,7 @@ - [Zero-Copy]() - [Access Control]() - [Building & Testing]() - - [Milestone Project - The Nightclub]() + - [Milestone Project - IDO Program](./anchor_in_depth/milestone_project_ido-program.md) - [Anchor BTS]() - [Dispatch]() - [The Discriminator]() diff --git a/src/anchor_in_depth/milestone_project_ido-program.md b/src/anchor_in_depth/milestone_project_ido-program.md new file mode 100644 index 0000000..dcdda22 --- /dev/null +++ b/src/anchor_in_depth/milestone_project_ido-program.md @@ -0,0 +1,1319 @@ +# Project IDO Program + +## Overview + +It is now time to implement the in-depth concepts you learned in section 4 of the book through a project. Create a new anchor workspace + +```bash +anchor init ido-program +``` + +As the name suggests, this is a program that helps projects do an Initial DEX Offering to retail investors. + +This program will be doing a couple of things. In an IDO, a project distributes its native tokens among willing investors. There are a few types of IDOs which are done. We will be implementing a fair launch IDO platform. In here, a project escrows the native tokens into a pool. When the IDO opens, investors come and deposit their fiat tokens into the pool. After the IDO is over, investors can claim their native tokens, the amount of which is calculated by the the pool percentage share of their deposit. The project can also finally withdraw all the deposited fiat tokens. + +We recommend keeping programs in a single `lib.rs` file. + +## Setting Up the Pool State + +Let’s think about what information we would want to store in our pool account. +We would want to know the authority of the pool. +The mint public keys of the redeemable intermediary token, native tokens, and fiat tokens are also necessary to ensure that the correct tokens are deposited in the pool. +We would need to know the associated token accounts of the pool authority which would store and distribute tokens. Additionally, we would need to know the number of tokens being distributed. +Finally, we would require the timestamps for the IDO and the bump associated with the pool PDA. +Add the following code at the bottom of the `lib.rs` file + +```rust +#[account] +pub struct PoolAccount { + /// Authority of the Pool + pub pool_authority: Pubkey, + + /// Mint of redeemable tokens (Intermediate tokens which will be exchanged for native tokens) + pub redeemable_mint: Pubkey, + + /// Mint of project tokens + pub native_mint: Pubkey, + + /// Mint of fiat tokens + pub fiat_mint: Pubkey, + + /// Token Account of Pool associated with the project token mint + pub pool_native: Pubkey, + + /// Token Account of Pool associated with fiat mint + pub pool_fiat: Pubkey, + + /// Total number of native tokens being distributed + pub total_native_tokens: u64, + + /// Unix timestamp for starting IDO + pub start_ido_ts: i64, + + /// Unix timestamp for ending IDO + pub end_ido_ts: i64, + + /// Unix timestamp for withdrawing fiat tokens from the pool + pub withdraw_fiat_ts: i64, + + /// Bump + pub bump: u8, +} + +impl PoolAccount { + pub const LEN: usize = DISCRIMINATOR_LENGTH // Discriminator Length + + PUBKEY_LENGTH // Pool Authority + + PUBKEY_LENGTH // Redeemable Mint + + PUBKEY_LENGTH // Fiat Mint + + PUBKEY_LENGTH // Pool Native Token Account + + PUBKEY_LENGTH // Native Mint + + PUBKEY_LENGTH // Pool fiat Token Account + + DATA_LENGTH_64 // Total Native Token Amount + + DATA_LENGTH_64 // Start IDO TS + + DATA_LENGTH_64 // End IDO TS + + DATA_LENGTH_64 // Withdraw Native TS + + DATA_LENGTH_8; // Bump +} + +const DISCRIMINATOR_LENGTH: usize = 8; +const PUBKEY_LENGTH: usize = 32; +const DATA_LENGTH_64: usize = 8; +const DATA_LENGTH_8: usize = 1; +``` + +This is all the data we store in `PoolAccount`. We also add the `impl` block for `PoolAccount` which calculates the amount of space required to pay rent. + +## Instructions and Handler Functions + +Let’s think about our program flow. The program will have 4 instructions. We should be able to initialize a pool that will hold information regarding the IDO. When the IDO starts, investors should be able to exchange their fiat tokens for the same number of intermediary tokens. After the IDO is over, investors should be able to exchange their intermediary tokens for the project’s tokens. Finally, the project should be able to redeem all the fiat tokens from the pool. + +### Initializing the Pool + +The first step is adding the instruction that will set up the pool account. We will be writing the `initialize_pool` handler function and the `InitializePool` accounts struct. + +```rust +#[derive(Accounts)] +pub struct InitializePool<'info> { + #[account(init, payer = authority, space = PoolAccount::LEN)] + pub pool: Box>, + + /// CHECK: This is not dangerous + #[account(mut)] + pub pool_signer: AccountInfo<'info>, + + #[account( + constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key), + constraint = redeemable_mint.supply == 0 + )] + pub redeemable_mint: Box>, + + #[account(constraint = fiat_mint.decimals == redeemable_mint.decimals)] + pub fiat_mint: Box>, + + #[account(constraint = pool_native.mint == *native_mint.to_account_info().key)] + pub native_mint: Box>, + + #[account(mut, constraint = pool_native.owner == *pool_signer.key)] + pub pool_native: Box>, + + #[account(constraint = pool_fiat.owner == *pool_signer.key)] + pub pool_fiat: Box>, + + #[account(mut)] + pub authority: Signer<'info>, + + #[account(mut)] + pub creator_native: Box>, + + pub token_program: Program<'info, Token>, + + pub rent: Sysvar<'info, Rent>, + + pub clock: Sysvar<'info, Clock>, + + pub system_program: Program<'info, System>, +} +``` + +Let’s look at the accounts we added + +- `pool`: We create the pool account with the init constraint. We add a payer by the name `authority`: which we add later in the account struct. And we also add the space needed to store the account which we already calculated in the `impl` block of `PoolAccount`. +- `pool_signer`: This is the authority that will have control of the pool. +- `redeemable_mint`: The mint address of the redeemable tokens. We add some constraints to check that the token has the correct configurations set. +- `fiat_mint`: The mint address of the fiat tokens. We use the constraint that the decimals of the fiat tokens and the redeemable tokens should be the same. +- `native_mint`: The mint address of the native token. +- `pool_native`: The token account owned by the pool signer that will hold all the native tokens. +- `pool_fiat`: The token account owned by the pool signer that will hold all the fiat tokens sent by the investor. +- `authority`: The transaction signer +- `creator_native`: The token account owned by the project which will send the native tokens to `pool_native` + +Now this code has a problem. We don’t actually have the `Mint`, `Token` and `TokenAccount` structs available in the current scope. + +For that, we need the dependency `anchor-spl`. Go into your `Cargo.toml` file inside the src folder and add in the dependency: + +```toml +[package] +name = "ido-program" +version = "0.1.0" +description = "Created with Anchor" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] +name = "ido_program" + +[features] +no-entrypoint = [] +no-idl = [] +no-log-ix-name = [] +cpi = ["no-entrypoint"] +default = [] + +[dependencies] +anchor-lang = "0.28.0" +anchor-spl = "0.28.0" +``` + +Now run the following command. + +```bash +anchor build +``` + +You will see errors, which are easy to fix. + +We can bring everything we need into scope. Import everything at the top of your `lib.rs` file. + +```rust +use anchor_lang::prelude::*; +use anchor_lang::solana_program::program_option::COption; +use anchor_spl::token::{self, Burn, Mint, MintTo, TokenAccount, Transfer, Token}; + +declare_id!("GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6"); + +//--snip-- +``` + +Now we can move forward with writing the handler function `initialize_pool` + +```rust +#[program] +pub mod ido_program { + use super::*; + + #[access_control(InitializePool::accounts(&ctx, bump) pre_ido_phase(&ctx, start_ido_ts))] + pub fn initialize_pool( + ctx: Context, + total_native_tokens: u64, + start_ido_ts: i64, + end_ido_ts: i64, + withdraw_fiat_ts: i64, + bump: u8, + ) -> Result<()> { + + if !(start_ido_ts < end_ido_ts + && end_ido_ts < withdraw_fiat_ts) + { + return Err(ErrorCode::NonSequentialTimestamps.into()); + } + + if total_native_tokens == 0 { + return Err(ErrorCode::InvalidParameter.into()); + } + + let pool = &mut ctx.accounts.pool; + + pool.pool_authority = *ctx.accounts.authority.key; + pool.redeemable_mint = ctx.accounts.redeemable_mint.key(); + pool.native_mint = ctx.accounts.native_mint.key(); + pool.fiat_mint = ctx.accounts.fiat_mint.key(); + pool.pool_native = ctx.accounts.pool_native.key(); + pool.pool_fiat = ctx.accounts.pool_fiat.key(); + pool.total_native_tokens = total_native_tokens; + pool.start_ido_ts = start_ido_ts; + pool.end_ido_ts = end_ido_ts; + pool.withdraw_fiat_ts = withdraw_fiat_ts; + + pool.bump = bump; + + //Transfer Native tokens from Creator to Pool Account + let cpi_accounts = Transfer { + from: ctx.accounts.creator_native.to_account_info(), + to: ctx.accounts.pool_native.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + token::transfer(cpi_ctx, total_native_tokens)?; + + Ok(()) + } +} +``` + +The first thing we see, even before the function definition is an access_control function being invoked. We will come to them at a later point in time. For now let’s move on and understand what is going on. + +The first thing we do after declaring the function signature is checking if the timestamps and the number of tokens given by the project are correct or not. If not, we return errors. Then we set all the fields of the `pool` structs. + +Now, we need to make a Cross Program Invocation to the Token Program to transfer tokens from the creator’s account to the pool account. + +We create a `Transfer` accounts instance. Here, we are basically declaring that tokens will be transferred from the `creator_native` token account to the `pool_native` token account, and the authority for that transfer will be the `authority` account. + +We create a new `CpiContext` with the token program and the `Transfer` accounts instance we created. + +And finally, we make a call to `token::transfer()` with the context and the number of tokens specified in the parameters. + +The code will not compile yet. This is because the function `pre_ido_phase()` we called under `access_control` and the custom `ErrorCode` s we wrote have not been defined yet. There is also no `accounts()` function under the `InitializePool` implementation block. + +Add this after the `InitializePool` account structure. + +```rust + //--snip-- + pub clock: Sysvar<'info, Clock>, + + pub system_program: Program<'info, System>, +} + +impl<'info> InitializePool<'info> { + fn accounts(ctx: &Context>, bump: u8) -> Result<()> { + let expected_signer = Pubkey::create_program_address( + &[ctx.accounts.pool_native.mint.as_ref(), &[bump]], + ctx.program_id, + ) + .map_err(|_| ErrorCode::InvalidBump)?; + if ctx.accounts.pool_signer.key != &expected_signer { + return Err(ErrorCode::InvalidBump.into()); + } + Ok(()) + } +} + +//--snip-- +``` + +The `accounts()` function here is checking if the `pool_signer` PDA passed in the accounts structure of `InitializePool` has the correct seeds or not. + +And finally, add this at the bottom of the file. + +```rust +//--snip-- + +#[error_code] +pub enum ErrorCode { + #[msg("Timestamps are not Sequential")] + NonSequentialTimestamps, + #[msg("Invalid Parameter")] + InvalidParameter, + #[msg("Invalid Bump")] + InvalidBump, + #[msg("IDO has not begun yet")] + IdoFuture +} + + +// Access Control Modifiers + +// IDO Starts in the Future +fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) -> Result<()> { + if !(ctx.accounts.clock.unix_timestamp < start_ido_ts) { + return Err(ErrorCode::IdoFuture.into()); + } + Ok(()) +} +``` + +Inside the `pre_ido_phase` function, we must ensure that the `start_ido_ts` timestamp parameter is greater than the current timestamp. + +Now, run `anchor build`. Now everything will compile correctly, with a warning of unused imports, which we can ignore. + +### Exchanging Investor’s Fiat for Redeemable Tokens + +Now we can add the instruction that will allow investors to deposit their fiat tokens and receive an equal number of redeemable tokens. We will write the handler function `exchange_fiat_for_redeemable()` and the `ExchangeFiatForRedeemable` + +Let’s start with `ExchangeFiatForRedeemable`. Add this code after the impl block of `InitializePool` + +```rust +#[derive(Accounts)] +pub struct ExchangeFiatForRedeemable<'info> { + #[account(mut, has_one = redeemable_mint, has_one = pool_fiat)] + pub pool: Box>, + + ///CHECK: This is not dangerous + #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] + pool_signer: AccountInfo<'info>, + + #[account( + mut, + constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + )] + pub redeemable_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + pub fiat_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] + pub pool_fiat: Account<'info, TokenAccount>, + + #[account(mut)] + pub authority: Signer<'info>, + + #[account(mut, constraint = investor_fiat.owner == *authority.key)] + pub investor_fiat: Account<'info, TokenAccount>, + + #[account(mut, constraint = investor_redeemable.owner == *authority.key)] + pub investor_redeemable: Account<'info, TokenAccount>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub clock: Sysvar<'info, Clock>, +} +``` + +We need the redeemable and fiat token mint accounts. This is because we need to know which tokens we will be dealing with in this instruction. + +We also need the fiat `TokenAccount`s owned by the pool_signer and the investor. This is because we need to know from which account to which account tokens need to be transferred. In this case, fiat tokens will be transferred from the investor’s fiat `TokenAccount` to pool_signer’s fiat `TokenAccount`. + +And finally, we need to know the investor’s redeemable `TokenAccount` to be able to mint redeemable token accounts to the investor. + +Let’s add the handler function. Add this after the `initialize_pool` function + +```rust +#[access_control(unrestricted_phase(&ctx))] + pub fn exchange_fiat_for_redeemable( + ctx: Context, + amount: u64, + ) -> Result<()> { + if amount == 0 { + return Err(ErrorCode::InvalidParameter.into()); + } + // While token::transfer will check this, we prefer a verbose error msg + if ctx.accounts.investor_fiat.amount < amount { + return Err(ErrorCode::LowFiat.into()); + } + + // Transfer investor's fiat to pool fiat account. + let cpi_accounts = Transfer { + from: ctx.accounts.investor_fiat.to_account_info(), + to: ctx.accounts.pool_fiat.to_account_info(), + authority: ctx.accounts.authority.to_account_info().clone(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + token::transfer(cpi_ctx, amount)?; + + // Mint Redeemable to investor Redeemable account. + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + let cpi_accounts = MintTo { + mint: ctx.accounts.redeemable_mint.to_account_info(), + to: ctx.accounts.investor_redeemable.to_account_info(), + authority: ctx.accounts.pool_signer.clone(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + token::mint_to(cpi_ctx, amount)?; + + Ok(()) + } +``` + +The handler function takes two parameters, the context, and the amount of fiat tokens to be sent. + +The first thing we do is check if the amount given as the parameter is 0 or not, and throw an error if it is. + +Then we check if the investor’s fiat TokenAccount has a lesser amount of tokens than the amount specified. Although the `token::transfer` call already checks it, we want a verbose error message. + +We create a new context with a `Transfer` object where we declare that tokens will be sent from the investor’s fiat TokenAccount to the pool’s fiat TokenAccount. + +We create a new `CpiContext` with the token program and the accounts context. + +We then invoke `token::transfer` to transfer `amount` number of fiat tokens + +Now we need a mechanism to track which investor deposited how much to the pool, since we don’t store the data anywhere. The easiest way to do that is to send an equal number of tokens to the investor which they can use to redeem the native tokens once the IDO is over (Hence the name redeemable tokens). The authority to mint these tokens rests with the `pool_signer` which we check in `redeemable_mint`'s constraint in the accounts struct. These tokens don’t hold any value, and exactly the same number of redeemable tokens will be minted as the number of fiat tokens sent to the pool. + +Now, since a PDA needs to sign a transaction to mint redeemable tokens, we have to invoke `CpiContext::new_with_signer()` instead of `CpiContext::new()`. And to get the signer, we need to construct the PDA seeds. + +We construct the seeds with the `native_mint` address and bump we already store in the pool account to get the signer. + +A `MintTo` context is created. Here we need to tell the program the mint account of the tokens that will be minted, the investor’s token account to where the redeemable tokens will be sent, and the authority of the transaction which is the `pool_signer`. + +We invoke `CpiContext::new_with_signer()` with the token program, the accounts context and the signer which we constructed. + +Finally, we call `token::mint_to` to send an equal number of redeemable tokens to the investor’s token account as the number of fiat tokens sent to the pool. + +As a final step, add the errors and access control function, and build the program. + +```rust +#[error_code] +pub enum ErrorCode { + #[msg("Timestamps are not Sequential")] + NonSequentialTimestamps, + #[msg("Invalid Parameter")] + InvalidParameter, + #[msg("Invalid Bump")] + InvalidBump, + #[msg("IDO has not begun yet")] + IdoFuture, + #[msg("Not the correct time to invest")] + WrongInvestingTime, + #[msg("Insufficient Fiat Tokens")] + LowFiat +} + +// Access Control Modifiers + +// IDO Starts in the Future +fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) -> Result<()> { + if !(ctx.accounts.clock.unix_timestamp < start_ido_ts) { + return Err(ErrorCode::IdoFuture.into()); + } + Ok(()) +} + +// Unrestricted Phase +fn unrestricted_phase<'info>(ctx: &Context>) -> Result<()> { + if !( + ctx.accounts.pool.start_ido_ts < ctx.accounts.clock.unix_timestamp + && + ctx.accounts.pool.end_ido_ts > ctx.accounts.clock.unix_timestamp + ) { + return Err(ErrorCode::WrongInvestingTime.into()); + } + Ok(()) +} +``` + +The access control function `unrestricted_phase` ensures that deposits are limited between the start_ido_ts and end_ido_ts. + +Run `anchor build` + +### Exchanging Investor’s Redeemable Tokens for Native Tokens + +The instruction and handler function for this will look very similar to just the previous one. +Let’s look at `ExchangeRedeemableForNative`. + +```rust +#[derive(Accounts)] +pub struct ExchangeRedeemableForNative<'info> { + #[account(has_one = redeemable_mint, has_one = pool_native)] + pub pool: Box>, + + /// CHECK: This is not dangerous + #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] + pool_signer: AccountInfo<'info>, + + #[account( + mut, + constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + )] + pub redeemable_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_native.owner == *pool_signer.key)] + pub pool_native: Account<'info, TokenAccount>, + + #[account(mut)] + pub authority: Signer<'info>, + + #[account(mut, constraint = investor_native.owner == *authority.key)] + pub investor_native: Account<'info, TokenAccount>, + + #[account(mut, constraint = investor_redeemable.owner == *authority.key)] + pub investor_redeemable: Account<'info, TokenAccount>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub clock: Sysvar<'info, Clock>, +} +``` + +This is the same as the previous instruction, the only difference being we are dealing with the native and redeemable tokens this time. + +We can move on to the `exchange_redeemable_for_native` handler function. + +```rust +#[access_control(ido_over(&ctx.accounts.pool, &ctx.accounts.clock))] + pub fn exchange_redeemable_for_native( + ctx: Context, + ) -> Result<()> { + let native_amount = (ctx.accounts.investor_redeemable.amount as u128) + .checked_mul(ctx.accounts.pool_native.amount as u128) + .unwrap() + .checked_div(ctx.accounts.redeemable_mint.supply as u128) + .unwrap(); + + let cpi_accounts = Burn { + mint: ctx.accounts.redeemable_mint.to_account_info(), + from: ctx.accounts.investor_redeemable.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); + + token::burn(cpi_ctx, ctx.accounts.investor_redeemable.amount)?; + + + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + + let cpi_accounts = Transfer { + from: ctx.accounts.pool_native.to_account_info(), + to: ctx.accounts.investor_native.to_account_info(), + authority: ctx.accounts.pool_signer.to_account_info(), + }; + + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + + token::transfer(cpi_ctx, native_amount as u64)?; + + Ok(()) + } +``` + +Here we go through a few new steps. + +The first step is to calculate how much native tokens the investor will be receiving. The simple formula is + +(Amount of Redeemable Tokens with Investor \* Amount of Native Tokens in Pool) / Supply of Redeemable Tokens + +The next step is to `Burn` the investor’s redeemable tokens. We create a `Burn` instance where we declare the redeemable token mint, investor’s token account from where the tokens will be burned and the authority. + +We create a new `CpiContext` with the token program and invoke `token::burn()`. + +We then move on to calculate the seeds of the pool_signer and create a `Transfer` instance. Finally, we transfer the calculated amount of native tokens from `pool_native` to `investor_native`. + +We are not done yet, the final thing left to do is write the access control function `ido_over()` + +```rust +//iDO Over +fn ido_over<'info>( + pool_account: &Account<'info, PoolAccount>, + clock: &Sysvar<'info, Clock>, +) -> Result<()> { + if !(pool_account.end_ido_ts < clock.unix_timestamp) { + return Err(ErrorCode::IdoNotOver.into()); + } + Ok(()) +} +``` + +It’s a similar check we perform based on the timestamps. + +Update the error codes + +```rust +#[error_code] +pub enum ErrorCode { + #[msg("Timestamps are not Sequential")] + NonSequentialTimestamps, + #[msg("Invalid Parameter")] + InvalidParameter, + #[msg("Invalid Bump")] + InvalidBump, + #[msg("IDO has not begun yet")] + IdoFuture, + #[msg("Not the correct time to invest")] + WrongInvestingTime, + #[msg("Insufficient Fiat Tokens")] + LowFiat, + #[msg("IDO has not ended yet")] + IdoNotOver +} +``` + +Run `anchor build`. You will see now that the warnings have disappeared. + +### Withdrawing Fiat Tokens From the Pool + +Now the final piece of the program that’s left is allowing the project to withdraw funds from the pool. Add this in the accounts struct + +```rust +#[derive(Accounts)] +pub struct WithdrawPoolFiat<'info> { + #[account(has_one = pool_fiat)] + pub pool: Box>, + + ///CHECK: This is not dangerous + #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] + pub pool_signer: AccountInfo<'info>, + + #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + pub fiat_mint: Account<'info, Mint>, + + #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] + pub pool_fiat: Account<'info, TokenAccount>, + + #[account(mut)] + pub payer: Signer<'info>, + + #[account(mut)] + pub creator_fiat: Account<'info, TokenAccount>, + + #[account(constraint = token_program.key == &token::ID)] + pub token_program: Program<'info, Token>, + + pub clock: Sysvar<'info, Clock>, +} +``` + +Here we need the project authority to sign the transaction to withdraw the pool fiat tokens. + +Add in the handler function `withdraw_pool_fiat` + +```rust +#[access_control(can_withdraw_fiat(&ctx.accounts.pool, &ctx.accounts.clock))] + pub fn withdraw_pool_fiat(ctx: Context) -> Result<()> { + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + let cpi_accounts = Transfer { + from: ctx.accounts.pool_fiat.to_account_info(), + to: ctx.accounts.creator_fiat.to_account_info(), + authority: ctx.accounts.pool_signer.to_account_info(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; + + Ok(()) + } +``` + +Again, here we do a similar thing. Calculate the signer seeds of pool_signer, Create a new `CpiContext` with the signer and invoke `token::transfer()` to transfer all fiat tokens from the pool to the project’s fiat `TokenAccount` + +Add the access control function + +```rust +//Can Withdraw fiat +fn can_withdraw_fiat<'info>( + pool_account: &Account<'info, PoolAccount>, + clock: &Sysvar<'info, Clock>, +) -> Result<()> { + if !(pool_account.withdraw_fiat_ts < clock.unix_timestamp) { + return Err(ErrorCode::CannotWithdrawYet.into()); + } + Ok(()) +} +``` + +And finally, update the error codes. + +```rust +#[error_code] +pub enum ErrorCode { + #[msg("Timestamps are not Sequential")] + NonSequentialTimestamps, + #[msg("Invalid Parameter")] + InvalidParameter, + #[msg("Invalid Bump")] + InvalidBump, + #[msg("IDO has not begun yet")] + IdoFuture, + #[msg("Not the correct time to invest")] + WrongInvestingTime, + #[msg("Insufficient Fiat Tokens")] + LowFiat, + #[msg("IDO has not ended yet")] + IdoNotOver, + #[msg("Cannot withdraw Fiat yet")] + CannotWithdrawYet +} +``` + +Run `anchor build` + +Now we can move onto testing our program. + +## Testing Our Program + +Head over to `tests/ido-program.ts`. It will look something like this. + +```ts +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { IdoProgram } from "../target/types/ido_program"; + +describe("test", () => { + // Configure the client to use the local cluster. + anchor.setProvider(anchor.AnchorProvider.env()); + + const program = anchor.workspace.Test as Program; + + it("Is initialized!", async () => { + // Add your test here. + const tx = await program.methods.initialize().rpc(); + console.log("Your transaction signature", tx); + }); +}); +``` + +We need the `@solana/spl-token` npm package to test out our functions. Install it. + +```bash +npm i @solana/spl-token +``` + +Now, the first thing we need to do is separate out the provider because we will need it afterward. And also import all the packages we need. + +```ts +import * as anchor from "@coral-xyz/anchor"; +import { Transaction, SystemProgram, PublicKey } from "@solana/web3.js"; +import { + createAccount, + createMint, + getAccount, + getOrCreateAssociatedTokenAccount, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { Program } from "@coral-xyz/anchor"; +import { assert } from "chai"; +import { IdoProgram } from "../target/types/ido_program"; + +describe("ido-program", () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.local(); + anchor.setProvider(provider); +// --snip-- +``` + +Great! Now the first test we will be to initialize everything so that we can run the actual tests. + +Let’s first declare all the variables we will need in the subsequent tests. + +```ts +describe("ido-program", () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.local(); + anchor.setProvider(provider); + + const program = anchor.workspace.IdoProgram as Program; + + let nativeTokenAmount = new anchor.BN(1000000); + + let fiatMint: PublicKey; + let redeemableMint: PublicKey; + let nativeMint: PublicKey; + + let projectFiat: PublicKey; + let projectNative: PublicKey; + + let investorFiat: PublicKey; + let investorNative: PublicKey; + let investorRedeemable: PublicKey; + + let poolNative: PublicKey; + let poolFiat: PublicKey; + + let poolSigner: PublicKey; + + let nowBn: anchor.BN; + let startIdoTs: anchor.BN; + let endIdoTs: anchor.BN; + let withDrawFiatTs: anchor.BN; + + const payer = anchor.web3.Keypair.generate(); + const mintAuthority = anchor.web3.Keypair.generate(); + + const project = anchor.web3.Keypair.generate(); + const investor = anchor.web3.Keypair.generate(); + + let pool = anchor.web3.Keypair.generate(); +}); +``` + +### Initialize the Program State + +Now let’s write the first test + +```ts + //--snip-- + + it("Can initialize the program state", async () => { + const transferSig = await provider.connection.requestAirdrop( + payer.publicKey, + 10000000000 + ); + + const latestBlockHash = await provider.connection.getLatestBlockhash(); + + await provider.connection.confirmTransaction({ + blockhash: latestBlockHash.blockhash, + lastValidBlockHeight: latestBlockHash.lastValidBlockHeight, + signature: transferSig, + }); + + const tx = new Transaction(); + + tx.add( + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: mintAuthority.publicKey, + lamports: 2000000000, + }), + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: project.publicKey, + lamports: 2000000000, + }), + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: investor.publicKey, + lamports: 2000000000, + }) + ); + + await provider.sendAndConfirm(tx, [payer]); + + fiatMint = await createMint( + provider.connection, + payer, + mintAuthority.publicKey, + undefined, + 0, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + nativeMint = await createMint( + provider.connection, + payer, + mintAuthority.publicKey, + undefined, + 0, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + projectFiat = await createAccount( + provider.connection, + payer, + fiatMint, + project.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + projectNative = await createAccount( + provider.connection, + payer, + nativeMint, + project.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + investorFiat = await createAccount( + provider.connection, + payer, + fiatMint, + investor.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + investorNative = await createAccount( + provider.connection, + payer, + nativeMint, + investor.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + payer, + nativeMint, + projectNative, + mintAuthority, + nativeTokenAmount.toNumber() + ); + + await mintTo( + provider.connection, + payer, + fiatMint, + investorFiat, + mintAuthority, + 10000 + ); + + const projectNativeTokenAccount = await getAccount( + provider.connection, + projectNative + ); + + assert.strictEqual( + projectNativeTokenAccount.amount.toString(), + nativeTokenAmount.toNumber().toString() + ); + }); +}) +``` + +The first thing we did was request 10 SOL to the `payer` account. Then we transfer 2 SOL each to `mintAuthority`, `project` and `investor` accounts so that they can pay rent and gas fees. + +Then we create dummy `fiatMint` and `nativeMint` accounts for testing purposes. + +We create `TokenAccount`s for these mints owned by the `project` and the `investor`. + +Finally we mint native tokens to the `project` and fiat tokens to the `investor`. + +Run `anchor test`. It should show 1 test passed. + +Now we are ready to test out our instructions. + +### Initialize the Pool + +```ts + //--snip-- + it("Can initialize the Pool", async () => { + const [_poolSigner, bump] = + anchor.web3.PublicKey.findProgramAddressSync( + [nativeMint.toBuffer()], + program.programId + ); + + poolSigner = _poolSigner; + + redeemableMint = await createMint( + provider.connection, + payer, + poolSigner, + undefined, + 0, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + investorRedeemable = await createAccount( + provider.connection, + payer, + redeemableMint, + investor.publicKey, + undefined, + undefined, + TOKEN_PROGRAM_ID + ); + + let poolNativeAccount = await getOrCreateAssociatedTokenAccount( + provider.connection, + payer, + nativeMint, + poolSigner, + true, + undefined, + undefined, + TOKEN_PROGRAM_ID, + undefined + ); + + poolNative = poolNativeAccount.address; + + let poolFiatAccount = await getOrCreateAssociatedTokenAccount( + provider.connection, + payer, + fiatMint, + poolSigner, + true, + undefined, + undefined, + TOKEN_PROGRAM_ID, + undefined + ); + + poolFiat = poolFiatAccount.address; + + nowBn = new anchor.BN(Date.now() / 1000); + startIdoTs = nowBn.add(new anchor.BN(10)); + endIdoTs = nowBn.add(new anchor.BN(20)); + withDrawFiatTs = nowBn.add(new anchor.BN(30)); + + await program.methods + .initializePool( + nativeTokenAmount, + startIdoTs, + endIdoTs, + withDrawFiatTs, + bump + ) + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + redeemableMint: redeemableMint, + fiatMint: fiatMint, + nativeMint: nativeMint, + poolNative: poolNative, + poolFiat: poolFiat, + authority: project.publicKey, + creatorNative: projectNative, + tokenProgram: TOKEN_PROGRAM_ID, + rent: anchor.web3.SYSVAR_RENT_PUBKEY, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + }) + .signers([pool, project]) + .rpc(); + + const poolNativeTokenAccount = await getAccount( + provider.connection, + poolNative + ); + + assert.strictEqual( + poolNativeTokenAccount.amount.toString(), + nativeTokenAmount.toNumber().toString() + ); + + const createdPool = await program.account.poolAccount.fetch( + pool.publicKey + ); + + assert.strictEqual( + createdPool.poolAuthority.toBase58(), + project.publicKey.toBase58() + ); + assert.strictEqual( + createdPool.redeemableMint.toBase58(), + redeemableMint.toBase58() + ); + assert.strictEqual( + createdPool.poolNative.toBase58(), + poolNative.toBase58() + ); + assert.strictEqual( + createdPool.nativeMint.toBase58(), + nativeMint.toBase58() + ); + assert.strictEqual( + createdPool.poolFiat.toBase58(), + poolFiat.toBase58() + ); + assert.strictEqual( + createdPool.totalNativeTokens.toNumber().toString(), + nativeTokenAmount.toString() + ); + assert.strictEqual( + createdPool.startIdoTs.toNumber().toString(), + startIdoTs.toString() + ); + assert.strictEqual( + createdPool.endIdoTs.toNumber().toString(), + endIdoTs.toString() + ); + assert.strictEqual( + createdPool.withdrawFiatTs.toNumber().toString(), + withDrawFiatTs.toString() + ); + }); +}) +``` + +First we derive the seeds using nativeMint to get the poolSigner PDA. Then we create the redeemable token mint account and the necessary token accounts owned by poolSIgner by calling `getOrCreateAssociatedTokenAccount()`. Then we set the different timestamps which we need in the program and call the `program.methods.initializePool()` method. We pass in all the accounts we defined in the accounts struct of our program and the signers, which are `pool` and `project`. + +Run `anchor test`. It should show 2 tests passed. + +### Exchange Investor Fiat for Redeemable Tokens + +```ts + //--snip-- + + let deposit = 5000; + + it("Can exchange investor Fiat for redeemable tokens", async () => { + if (Date.now() < startIdoTs.toNumber() * 1000) { + await sleep(startIdoTs.toNumber() * 1000 - Date.now() + 5000); + } + + await program.methods + .exchangeFiatForRedeemable(new anchor.BN(deposit)) + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + redeemableMint: redeemableMint, + fiatMint: fiatMint, + poolFiat: poolFiat, + authority: investor.publicKey, + investorFiat: investorFiat, + investorRedeemable: investorRedeemable, + tokenProgram: TOKEN_PROGRAM_ID, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + }) + .signers([investor]) + .rpc(); + + const poolFiatTokenAccount = await getAccount( + provider.connection, + poolFiat + ); + + assert.strictEqual( + poolFiatTokenAccount.amount.toString(), + deposit.toString() + ); + + const investorFiatTokenAccount = await getAccount( + provider.connection, + investorFiat + ); + + const investorRedeemableTokenAccount = await getAccount( + provider.connection, + investorRedeemable + ); + + assert.strictEqual( + investorFiatTokenAccount.amount.toString(), + (10000 - deposit).toString() + ); + + assert.strictEqual( + investorRedeemableTokenAccount.amount.toString(), + deposit.toString() + ); + }); +}) +``` + +We check for the current time and `sleep` before it is time to call the `exchangeFiatForRedeemable` method. We cannot call the method outside the stipulated time because of the access controls we added. If we try to call it outside the correct time, it will error out. + +But we have not added the `sleep` function yet. Add it at the end outside the `describe` block. + +```ts +//--snip-- +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} +``` + +Inside the test, we call the `exchangeFiatForRedeemable` program method, and then write the usual assert checks. + +Running `anchor test` should now show 3 tests passed. + +### Exchange User Redeemable For Native Tokens + +```ts + //--snip-- + it("Can exchange investor Redeemable tokens for Native tokens", async () => { + if (Date.now() < endIdoTs.toNumber() * 1000) { + await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 5000); + } + + await program.methods + .exchangeRedeemableForNative() + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + redeemableMint: redeemableMint, + poolNative: poolNative, + authority: investor.publicKey, + investorNative: investorNative, + investorRedeemable: investorRedeemable, + tokenProgram: TOKEN_PROGRAM_ID, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + }) + .signers([investor]) + .rpc(); + + const poolNativeTokenAccount = await getAccount( + provider.connection, + poolNative + ); + + assert.strictEqual(poolNativeTokenAccount.amount.toString(), "0"); + + const investorNativeTokenAccount = await getAccount( + provider.connection, + investorNative + ); + + const investorRedeemableTokenAccount = await getAccount( + provider.connection, + investorRedeemable + ); + + assert.strictEqual( + investorNativeTokenAccount.amount.toString(), + nativeTokenAmount.toString() + ); + + assert.strictEqual( + investorRedeemableTokenAccount.amount.toString(), + "0" + ); + }); +}) +``` + +Here also, we go through similar steps like the previous one. + +`anchor test` should now show 4 tests passed. + +### Withdraw Pool Fiat Tokens + +```ts + it("Can withdraw total Fiat from pool account", async () => { + if (Date.now() < withDrawFiatTs.toNumber() * 1000) { + await sleep(withDrawFiatTs.toNumber() * 1000 - Date.now() + 5000); + } + + await program.methods + .withdrawPoolFiat() + .accounts({ + pool: pool.publicKey, + poolSigner: poolSigner, + fiatMint: fiatMint, + poolFiat: poolFiat, + payer: project.publicKey, + creatorFiat: projectFiat, + tokenProgram: TOKEN_PROGRAM_ID, + clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, + }) + .signers([project]) + .rpc(); + + const poolFiatTokenAccount = await getAccount( + provider.connection, + poolFiat + ); + + assert.strictEqual(poolFiatTokenAccount.amount.toString(), "0"); + + const projectFiatTokenAccount = await getAccount( + provider.connection, + projectFiat + ); + + assert.strictEqual( + projectFiatTokenAccount.amount.toString(), + deposit.toString() + ); + }); +}); +``` + +Finally we test the `withdrawPoolFiat` program method, and that concludes all our tests. + +`anchor test` should now be showing 5 tests passed. + +And with that, our IDO Program project comes to a conclusion. From 25c3dfc98cc14d0afbba7747d4361fe53b0432f8 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:30:40 +0530 Subject: [PATCH 02/30] Update programs/ido-program/Anchor.toml Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/Anchor.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/Anchor.toml b/programs/ido-program/Anchor.toml index 73c3e78..ead154e 100644 --- a/programs/ido-program/Anchor.toml +++ b/programs/ido-program/Anchor.toml @@ -9,7 +9,7 @@ url = "https://api.apr.dev" [provider] cluster = "Localnet" -wallet = "/Users/catrovacer/.config/solana/id.json" +wallet = "~/.config/solana/id.json" [scripts] test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" From dadffb53d3795d87926fdf15641fa2b9cdbc2519 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:30:57 +0530 Subject: [PATCH 03/30] Update programs/ido-program/migrations/deploy.ts Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/migrations/deploy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/migrations/deploy.ts b/programs/ido-program/migrations/deploy.ts index 5e3df0d..82fb175 100644 --- a/programs/ido-program/migrations/deploy.ts +++ b/programs/ido-program/migrations/deploy.ts @@ -2,7 +2,7 @@ // single deploy script that's invoked from the CLI, injecting a provider // configured from the workspace's Anchor.toml. -const anchor = require("@project-serum/anchor"); +const anchor = require("@coral-xyz/anchor"); module.exports = async function (provider) { // Configure client to use the provider. From 643279daaa3b78277e0d7dc2ae3beac1cfc9e525 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:31:05 +0530 Subject: [PATCH 04/30] Update programs/ido-program/package.json Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/package.json b/programs/ido-program/package.json index bfab59d..7c25f74 100644 --- a/programs/ido-program/package.json +++ b/programs/ido-program/package.json @@ -4,7 +4,7 @@ "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" }, "dependencies": { - "@project-serum/anchor": "^0.26.0", + "@coral-xyz/anchor": "^0.28.0", "@solana/spl-token": "^0.3.8" }, "devDependencies": { From 70ad3a3bb8030863a03e3c020752c46772b8ae12 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:35:59 +0530 Subject: [PATCH 05/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 02053a6..6ca1317 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -50,7 +50,7 @@ pub mod ido_program { authority: ctx.accounts.authority.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, total_native_tokens)?; From 4e0a1a9bbc3e015e062a34a595a641afc8208af9 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:36:15 +0530 Subject: [PATCH 06/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 6ca1317..855dd1d 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -74,7 +74,7 @@ pub mod ido_program { let cpi_accounts = Transfer { from: ctx.accounts.investor_fiat.to_account_info(), to: ctx.accounts.pool_fiat.to_account_info(), - authority: ctx.accounts.authority.to_account_info().clone(), + authority: ctx.accounts.authority.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); From 7d49ddee39b4bf6631bda451a032aa5abd033cc0 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:36:29 +0530 Subject: [PATCH 07/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 855dd1d..4cc89b8 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -91,7 +91,7 @@ pub mod ido_program { to: ctx.accounts.investor_redeemable.to_account_info(), authority: ctx.accounts.pool_signer.clone(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); token::mint_to(cpi_ctx, amount)?; From e25f3a6e31e08a72aafbcd12530d726b4d0993c3 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:36:50 +0530 Subject: [PATCH 08/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 4cc89b8..2d71b01 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -114,7 +114,7 @@ pub mod ido_program { authority: ctx.accounts.authority.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::burn(cpi_ctx, ctx.accounts.investor_redeemable.amount)?; From 1a63466fa98b2e6c852986721de77a9a97740e29 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:37:06 +0530 Subject: [PATCH 09/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 2d71b01..b9e04a1 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -132,7 +132,7 @@ pub mod ido_program { authority: ctx.accounts.pool_signer.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); token::transfer(cpi_ctx, native_amount as u64)?; From 50bd96770c0b692cafafa52ea94ab208c19e1a08 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:37:19 +0530 Subject: [PATCH 10/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index b9e04a1..93838ba 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -152,7 +152,7 @@ pub mod ido_program { to: ctx.accounts.creator_fiat.to_account_info(), authority: ctx.accounts.pool_signer.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; From 04153616c93e075f2b8ff7ec5d4526314d7fcf07 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:37:35 +0530 Subject: [PATCH 11/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 93838ba..848c158 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -172,7 +172,7 @@ pub struct InitializePool<'info> { pub pool_signer: AccountInfo<'info>, #[account( - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key), + mint::authority = pool_signer constraint = redeemable_mint.supply == 0 )] pub redeemable_mint: Box>, From 6dd9b88ded9c0292b719e88df6c8f1bbd8b0f22f Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:37:53 +0530 Subject: [PATCH 12/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 848c158..3b4b99d 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -177,7 +177,7 @@ pub struct InitializePool<'info> { )] pub redeemable_mint: Box>, - #[account(constraint = fiat_mint.decimals == redeemable_mint.decimals)] + #[account(mint::decimals = redeemable_mint.decimals)] pub fiat_mint: Box>, #[account(constraint = pool_native.mint == *native_mint.to_account_info().key)] From cb7160ed6b83ab6160a7d19d3c0f010021fee30e Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:38:05 +0530 Subject: [PATCH 13/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 3b4b99d..b3fecba 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -180,7 +180,7 @@ pub struct InitializePool<'info> { #[account(mint::decimals = redeemable_mint.decimals)] pub fiat_mint: Box>, - #[account(constraint = pool_native.mint == *native_mint.to_account_info().key)] + #[account(address = pool_native.mint)] pub native_mint: Box>, #[account(mut, constraint = pool_native.owner == *pool_signer.key)] From c08ed2b438f80011d5029ca6a92cfe49f4aa0269 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:38:34 +0530 Subject: [PATCH 14/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index b3fecba..1f6d5bb 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -195,7 +195,6 @@ pub struct InitializePool<'info> { #[account(mut)] pub creator_native: Box>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, pub rent: Sysvar<'info, Rent>, From 3251df72f58a86e80c1f2caa4b6245ea8b8a83ad Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:39:19 +0530 Subject: [PATCH 15/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 1f6d5bb..4d6e3a9 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -229,7 +229,7 @@ pub struct ExchangeFiatForRedeemable<'info> { #[account( mut, - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + mint::authority = pool_signer )] pub redeemable_mint: Account<'info, Mint>, From 2ae1d0852c724bf25b8f887b64d203d57638ca4b Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:42:27 +0530 Subject: [PATCH 16/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 4d6e3a9..cb508e1 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -233,7 +233,7 @@ pub struct ExchangeFiatForRedeemable<'info> { )] pub redeemable_mint: Account<'info, Mint>, - #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + #[account(mut, address = pool_fiat.mint)] pub fiat_mint: Account<'info, Mint>, #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] From 8741820ca0c7ecfb2ba02bef77a3a37736dc3c7a Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:42:57 +0530 Subject: [PATCH 17/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index cb508e1..ac85356 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -248,7 +248,6 @@ pub struct ExchangeFiatForRedeemable<'info> { #[account(mut, constraint = investor_redeemable.owner == *authority.key)] pub investor_redeemable: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, pub clock: Sysvar<'info, Clock>, From be2ad5c64444c6095daa8b1e9dba452e1e4b75e6 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:23:04 +0530 Subject: [PATCH 18/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index ac85356..930acaf 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -197,7 +197,6 @@ pub struct InitializePool<'info> { pub token_program: Program<'info, Token>, - pub rent: Sysvar<'info, Rent>, pub clock: Sysvar<'info, Clock>, From 4dc6be525e5c59d9c51106ed23a7c81f24f22cdb Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:23:18 +0530 Subject: [PATCH 19/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 930acaf..369eb5d 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -198,7 +198,6 @@ pub struct InitializePool<'info> { pub token_program: Program<'info, Token>, - pub clock: Sysvar<'info, Clock>, pub system_program: Program<'info, System>, } From 7a6f9bc9332145eb5d6f3bfc551df753b4089f76 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:23:43 +0530 Subject: [PATCH 20/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 369eb5d..17973c9 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -248,7 +248,6 @@ pub struct ExchangeFiatForRedeemable<'info> { pub token_program: Program<'info, Token>, - pub clock: Sysvar<'info, Clock>, } #[derive(Accounts)] From 6209c170e059bdfa1caa2f3933eed1a39352dfb0 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:24:13 +0530 Subject: [PATCH 21/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 17973c9..a4df8ef 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -261,7 +261,7 @@ pub struct ExchangeRedeemableForNative<'info> { #[account( mut, - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + mint::authority = pool_signer )] pub redeemable_mint: Account<'info, Mint>, From dc807e347e1eb65f8032f0080ebf54cd6e237ea0 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:25:01 +0530 Subject: [PATCH 22/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index a4df8ef..190e4c5 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -292,7 +292,7 @@ pub struct WithdrawPoolFiat<'info> { #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] pub pool_signer: AccountInfo<'info>, - #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + #[account(adress = pool_fiat.mint)] pub fiat_mint: Account<'info, Mint>, #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] From 82905f5c7bdf18e9ea7b7a47431bb2b2806dd734 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:25:37 +0530 Subject: [PATCH 23/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 190e4c5..5e94d7f 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -277,7 +277,6 @@ pub struct ExchangeRedeemableForNative<'info> { #[account(mut, constraint = investor_redeemable.owner == *authority.key)] pub investor_redeemable: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, pub clock: Sysvar<'info, Clock>, From f07e56ea70aea7918d8d6ad26dfe560b237da5c3 Mon Sep 17 00:00:00 2001 From: Swarnab Garang <72680953+0xcatrovacer@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:26:09 +0530 Subject: [PATCH 24/30] Update programs/ido-program/programs/ido-program/src/lib.rs Co-authored-by: acheron <98934430+acheroncrypto@users.noreply.github.com> --- programs/ido-program/programs/ido-program/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 5e94d7f..fd59886 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -303,7 +303,6 @@ pub struct WithdrawPoolFiat<'info> { #[account(mut)] pub creator_fiat: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, pub clock: Sysvar<'info, Clock>, From 817bc8904c09bf2c51b93ab8a197aa1e8da52a32 Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Sun, 2 Jul 2023 10:48:45 +0530 Subject: [PATCH 25/30] changes made according to review --- .../programs/ido-program/src/lib.rs | 130 +++++++----------- programs/ido-program/tests/ido-program.ts | 57 ++------ programs/ido-program/tsconfig.json | 19 ++- programs/ido-program/yarn.lock | 64 ++++----- 4 files changed, 102 insertions(+), 168 deletions(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index ac85356..8e2ed10 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -1,6 +1,6 @@ use anchor_lang::prelude::*; use anchor_lang::solana_program::program_option::COption; -use anchor_spl::token::{self, Burn, Mint, MintTo, TokenAccount, Transfer, Token}; +use anchor_spl::token::{self, Burn, Mint, MintTo, Token, TokenAccount, Transfer}; declare_id!("GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6"); @@ -8,19 +8,16 @@ declare_id!("GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6"); pub mod ido_program { use super::*; - #[access_control(InitializePool::accounts(&ctx, bump) pre_ido_phase(&ctx, start_ido_ts))] + #[access_control(InitializePool::accounts(&ctx, bump) pre_ido_phase(start_ido_ts))] pub fn initialize_pool( ctx: Context, - total_native_tokens: u64, - start_ido_ts: i64, + total_native_tokens: u64, + start_ido_ts: i64, end_ido_ts: i64, withdraw_fiat_ts: i64, bump: u8, ) -> Result<()> { - - if !(start_ido_ts < end_ido_ts - && end_ido_ts < withdraw_fiat_ts) - { + if !(start_ido_ts < end_ido_ts && end_ido_ts < withdraw_fiat_ts) { return Err(ErrorCode::NonSequentialTimestamps.into()); } @@ -69,7 +66,7 @@ pub mod ido_program { if ctx.accounts.investor_fiat.amount < amount { return Err(ErrorCode::LowFiat.into()); } - + // Transfer investor's fiat to pool fiat account. let cpi_accounts = Transfer { from: ctx.accounts.investor_fiat.to_account_info(), @@ -79,11 +76,11 @@ pub mod ido_program { let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, amount)?; - + // Mint Redeemable to investor Redeemable account. let seeds = &[ - ctx.accounts.pool.native_mint.as_ref(), - &[ctx.accounts.pool.bump], + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], ]; let signer = &[&seeds[..]]; let cpi_accounts = MintTo { @@ -94,19 +91,17 @@ pub mod ido_program { let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); token::mint_to(cpi_ctx, amount)?; - + Ok(()) } - #[access_control(ido_over(&ctx.accounts.pool, &ctx.accounts.clock))] - pub fn exchange_redeemable_for_native( - ctx: Context, - ) -> Result<()> { + #[access_control(ido_over(&ctx.accounts.pool))] + pub fn exchange_redeemable_for_native(ctx: Context) -> Result<()> { let native_amount = (ctx.accounts.investor_redeemable.amount as u128) - .checked_mul(ctx.accounts.pool_native.amount as u128) - .unwrap() - .checked_div(ctx.accounts.redeemable_mint.supply as u128) - .unwrap(); + .checked_mul(ctx.accounts.pool_native.amount as u128) + .unwrap() + .checked_div(ctx.accounts.redeemable_mint.supply as u128) + .unwrap(); let cpi_accounts = Burn { mint: ctx.accounts.redeemable_mint.to_account_info(), @@ -119,7 +114,6 @@ pub mod ido_program { token::burn(cpi_ctx, ctx.accounts.investor_redeemable.amount)?; - let seeds = &[ ctx.accounts.pool.native_mint.as_ref(), &[ctx.accounts.pool.bump], @@ -140,27 +134,25 @@ pub mod ido_program { Ok(()) } - #[access_control(can_withdraw_fiat(&ctx.accounts.pool, &ctx.accounts.clock))] - pub fn withdraw_pool_fiat(ctx: Context) -> Result<()> { - let seeds = &[ - ctx.accounts.pool.native_mint.as_ref(), - &[ctx.accounts.pool.bump], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = Transfer { - from: ctx.accounts.pool_fiat.to_account_info(), - to: ctx.accounts.creator_fiat.to_account_info(), - authority: ctx.accounts.pool_signer.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; - - Ok(()) - } -} - + #[access_control(can_withdraw_fiat(&ctx.accounts.pool))] + pub fn withdraw_pool_fiat(ctx: Context) -> Result<()> { + let seeds = &[ + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], + ]; + let signer = &[&seeds[..]]; + let cpi_accounts = Transfer { + from: ctx.accounts.pool_fiat.to_account_info(), + to: ctx.accounts.creator_fiat.to_account_info(), + authority: ctx.accounts.pool_signer.to_account_info(), + }; + let cpi_program = ctx.accounts.token_program.to_account_info(); + let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); + token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; + Ok(()) + } +} #[derive(Accounts)] pub struct InitializePool<'info> { @@ -172,7 +164,7 @@ pub struct InitializePool<'info> { pub pool_signer: AccountInfo<'info>, #[account( - mint::authority = pool_signer + mint::authority = pool_signer, constraint = redeemable_mint.supply == 0 )] pub redeemable_mint: Box>, @@ -197,10 +189,6 @@ pub struct InitializePool<'info> { pub token_program: Program<'info, Token>, - pub rent: Sysvar<'info, Rent>, - - pub clock: Sysvar<'info, Clock>, - pub system_program: Program<'info, System>, } @@ -225,7 +213,7 @@ pub struct ExchangeFiatForRedeemable<'info> { ///CHECK: This is not dangerous #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] - pool_signer: AccountInfo<'info>, + pub pool_signer: AccountInfo<'info>, #[account( mut, @@ -249,8 +237,6 @@ pub struct ExchangeFiatForRedeemable<'info> { pub investor_redeemable: Account<'info, TokenAccount>, pub token_program: Program<'info, Token>, - - pub clock: Sysvar<'info, Clock>, } #[derive(Accounts)] @@ -260,7 +246,7 @@ pub struct ExchangeRedeemableForNative<'info> { /// CHECK: This is not dangerous #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] - pool_signer: AccountInfo<'info>, + pub pool_signer: AccountInfo<'info>, #[account( mut, @@ -282,8 +268,6 @@ pub struct ExchangeRedeemableForNative<'info> { #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, - - pub clock: Sysvar<'info, Clock>, } #[derive(Accounts)] @@ -309,8 +293,6 @@ pub struct WithdrawPoolFiat<'info> { #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, - - pub clock: Sysvar<'info, Clock>, } #[account] @@ -320,28 +302,28 @@ pub struct PoolAccount { /// Mint of redeemable tokens (Intermediate tokens which will be exchanged for native tokens) pub redeemable_mint: Pubkey, - + /// Mint of project tokens pub native_mint: Pubkey, - + /// Mint of fiat tokens pub fiat_mint: Pubkey, /// Token Account of Pool associated with the project token mint pub pool_native: Pubkey, - + /// Token Account of Pool associated with fiat mint pub pool_fiat: Pubkey, /// Total number of native tokens being distributed pub total_native_tokens: u64, - + /// Unix timestamp for starting IDO pub start_ido_ts: i64, - + /// Unix timestamp for ending IDO pub end_ido_ts: i64, - + /// Unix timestamp for withdrawing Fiat from pool pub withdraw_fiat_ts: i64, @@ -349,7 +331,6 @@ pub struct PoolAccount { pub bump: u8, } - impl PoolAccount { pub const LEN: usize = DISCRIMINATOR_LENGTH // Discriminator Length + PUBKEY_LENGTH // Pool Authority @@ -362,7 +343,7 @@ impl PoolAccount { + DATA_LENGTH_64 // Start IDO TS + DATA_LENGTH_64 // End IDO TS + DATA_LENGTH_64 // Withdraw Fiat TS - + DATA_LENGTH_8; // Bump + + DATA_LENGTH_8; // Bump } const DISCRIMINATOR_LENGTH: usize = 8; @@ -387,15 +368,14 @@ pub enum ErrorCode { #[msg("IDO has not ended yet")] IdoNotOver, #[msg("Cannot withdraw Fiat yet")] - CannotWithdrawYet + CannotWithdrawYet, } - // Access Control Modifiers // IDO Starts in the Future -fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) -> Result<()> { - if !(ctx.accounts.clock.unix_timestamp < start_ido_ts) { +fn pre_ido_phase<'info>(start_ido_ts: i64) -> Result<()> { + if !(Clock::get().unwrap().unix_timestamp < start_ido_ts) { return Err(ErrorCode::IdoFuture.into()); } Ok(()) @@ -403,11 +383,9 @@ fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) // Unrestricted Phase fn unrestricted_phase<'info>(ctx: &Context>) -> Result<()> { - if !( - ctx.accounts.pool.start_ido_ts < ctx.accounts.clock.unix_timestamp - && - ctx.accounts.pool.end_ido_ts > ctx.accounts.clock.unix_timestamp - ) { + if !(ctx.accounts.pool.start_ido_ts < Clock::get().unwrap().unix_timestamp + && ctx.accounts.pool.end_ido_ts > Clock::get().unwrap().unix_timestamp) + { return Err(ErrorCode::WrongInvestingTime.into()); } Ok(()) @@ -416,9 +394,8 @@ fn unrestricted_phase<'info>(ctx: &Context>) -> //iDO Over fn ido_over<'info>( pool_account: &Account<'info, PoolAccount>, - clock: &Sysvar<'info, Clock>, ) -> Result<()> { - if !(pool_account.end_ido_ts < clock.unix_timestamp) { + if !(pool_account.end_ido_ts < Clock::get().unwrap().unix_timestamp) { return Err(ErrorCode::IdoNotOver.into()); } Ok(()) @@ -427,10 +404,9 @@ fn ido_over<'info>( //Can Withdraw fiat fn can_withdraw_fiat<'info>( pool_account: &Account<'info, PoolAccount>, - clock: &Sysvar<'info, Clock>, ) -> Result<()> { - if !(pool_account.withdraw_fiat_ts < clock.unix_timestamp) { + if !(pool_account.withdraw_fiat_ts < Clock::get().unwrap().unix_timestamp) { return Err(ErrorCode::CannotWithdrawYet.into()); } Ok(()) -} \ No newline at end of file +} diff --git a/programs/ido-program/tests/ido-program.ts b/programs/ido-program/tests/ido-program.ts index 3853f14..ef4f369 100644 --- a/programs/ido-program/tests/ido-program.ts +++ b/programs/ido-program/tests/ido-program.ts @@ -1,4 +1,4 @@ -import * as anchor from "@project-serum/anchor"; +import * as anchor from "@coral-xyz/anchor"; import { Transaction, SystemProgram, PublicKey } from "@solana/web3.js"; import { createAccount, @@ -8,7 +8,7 @@ import { mintTo, TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import { Program } from "@project-serum/anchor"; +import { Program } from "@coral-xyz/anchor"; import { assert } from "chai"; import { IdoProgram } from "../target/types/ido_program"; @@ -90,62 +90,44 @@ describe("ido-program", () => { provider.connection, payer, mintAuthority.publicKey, - undefined, - 0, - undefined, - undefined, - TOKEN_PROGRAM_ID + null, + 0 ); nativeMint = await createMint( provider.connection, payer, mintAuthority.publicKey, - undefined, - 0, - undefined, - undefined, - TOKEN_PROGRAM_ID + null, + 0 ); projectFiat = await createAccount( provider.connection, payer, fiatMint, - project.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + project.publicKey ); projectNative = await createAccount( provider.connection, payer, nativeMint, - project.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + project.publicKey ); investorFiat = await createAccount( provider.connection, payer, fiatMint, - investor.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + investor.publicKey ); investorNative = await createAccount( provider.connection, payer, nativeMint, - investor.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + investor.publicKey ); await mintTo( @@ -190,21 +172,15 @@ describe("ido-program", () => { provider.connection, payer, poolSigner, - undefined, - 0, - undefined, - undefined, - TOKEN_PROGRAM_ID + null, + 0 ); investorRedeemable = await createAccount( provider.connection, payer, redeemableMint, - investor.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + investor.publicKey ); let poolNativeAccount = await getOrCreateAssociatedTokenAccount( @@ -213,9 +189,6 @@ describe("ido-program", () => { nativeMint, poolSigner, true, - undefined, - undefined, - TOKEN_PROGRAM_ID, undefined ); @@ -227,9 +200,6 @@ describe("ido-program", () => { fiatMint, poolSigner, true, - undefined, - undefined, - TOKEN_PROGRAM_ID, undefined ); @@ -259,7 +229,6 @@ describe("ido-program", () => { authority: project.publicKey, creatorNative: projectNative, tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }) diff --git a/programs/ido-program/tsconfig.json b/programs/ido-program/tsconfig.json index 558b83e..ab7d0c1 100644 --- a/programs/ido-program/tsconfig.json +++ b/programs/ido-program/tsconfig.json @@ -1,11 +1,10 @@ { - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } - } - \ No newline at end of file + "compilerOptions": { + "types": ["mocha", "chai"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true + } +} diff --git a/programs/ido-program/yarn.lock b/programs/ido-program/yarn.lock index 7c7a9a3..7f94270 100644 --- a/programs/ido-program/yarn.lock +++ b/programs/ido-program/yarn.lock @@ -9,32 +9,12 @@ dependencies: "regenerator-runtime" "^0.13.11" -"@coral-xyz/borsh@^0.26.0": - "integrity" "sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==" - "resolved" "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.26.0.tgz" - "version" "0.26.0" +"@coral-xyz/anchor@^0.28.0": + "integrity" "sha512-kQ02Hv2ZqxtWP30WN1d4xxT4QqlOXYDxmEd3k/bbneqhV3X5QMO4LAtoUFs7otxyivOgoqam5Il5qx81FuI4vw==" + "resolved" "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.28.0.tgz" + "version" "0.28.0" dependencies: - "bn.js" "^5.1.2" - "buffer-layout" "^1.2.0" - -"@noble/curves@^1.0.0": - "integrity" "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==" - "resolved" "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" - "version" "1.1.0" - dependencies: - "@noble/hashes" "1.3.1" - -"@noble/hashes@^1.3.0", "@noble/hashes@1.3.1": - "integrity" "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" - "resolved" "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" - "version" "1.3.1" - -"@project-serum/anchor@^0.26.0": - "integrity" "sha512-Nq+COIjE1135T7qfnOHEn7E0q39bQTgXLFk837/rgFe6Hkew9WML7eHsS+lSYD2p3OJaTiUOHTAq1lHy36oIqQ==" - "resolved" "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.26.0.tgz" - "version" "0.26.0" - dependencies: - "@coral-xyz/borsh" "^0.26.0" + "@coral-xyz/borsh" "^0.28.0" "@solana/web3.js" "^1.68.0" "base64-js" "^1.5.1" "bn.js" "^5.1.2" @@ -50,6 +30,26 @@ "superstruct" "^0.15.4" "toml" "^3.0.0" +"@coral-xyz/borsh@^0.28.0": + "integrity" "sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ==" + "resolved" "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.28.0.tgz" + "version" "0.28.0" + dependencies: + "bn.js" "^5.1.2" + "buffer-layout" "^1.2.0" + +"@noble/curves@^1.0.0": + "integrity" "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==" + "resolved" "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz" + "version" "1.1.0" + dependencies: + "@noble/hashes" "1.3.1" + +"@noble/hashes@^1.3.0", "@noble/hashes@1.3.1": + "integrity" "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + "resolved" "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz" + "version" "1.3.1" + "@solana/buffer-layout-utils@^0.2.0": "integrity" "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==" "resolved" "https://registry.npmjs.org/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz" @@ -787,12 +787,7 @@ "yargs-parser" "20.2.4" "yargs-unparser" "2.0.0" -"ms@^2.0.0": - "integrity" "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - "version" "2.1.3" - -"ms@2.1.2": +"ms@^2.0.0", "ms@2.1.2": "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" "version" "2.1.2" @@ -1144,12 +1139,7 @@ "resolved" "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" "version" "5.0.8" -"yargs-parser@^20.2.2": - "integrity" "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" - "version" "20.2.9" - -"yargs-parser@20.2.4": +"yargs-parser@^20.2.2", "yargs-parser@20.2.4": "integrity" "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" "resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" "version" "20.2.4" From eef0351142a4b5f5b3e616afe19c06c103b8a107 Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Sun, 2 Jul 2023 10:53:36 +0530 Subject: [PATCH 26/30] fixed issues in .md file --- .../milestone_project_ido-program.md | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/anchor_in_depth/milestone_project_ido-program.md b/src/anchor_in_depth/milestone_project_ido-program.md index dcdda22..743efae 100644 --- a/src/anchor_in_depth/milestone_project_ido-program.md +++ b/src/anchor_in_depth/milestone_project_ido-program.md @@ -8,7 +8,7 @@ It is now time to implement the in-depth concepts you learned in section 4 of th anchor init ido-program ``` -As the name suggests, this is a program that helps projects do an Initial DEX Offering to retail investors. +As the name suggests, this is a program that helps projects do an Initial DEX Offering to investors. This program will be doing a couple of things. In an IDO, a project distributes its native tokens among willing investors. There are a few types of IDOs which are done. We will be implementing a fair launch IDO platform. In here, a project escrows the native tokens into a pool. When the IDO opens, investors come and deposit their fiat tokens into the pool. After the IDO is over, investors can claim their native tokens, the amount of which is calculated by the the pool percentage share of their deposit. The project can also finally withdraw all the deposited fiat tokens. @@ -455,11 +455,11 @@ pub enum ErrorCode { NonSequentialTimestamps, #[msg("Invalid Parameter")] InvalidParameter, - #[msg("Invalid Bump")] + #[msg("Invalid Bump")] InvalidBump, #[msg("IDO has not begun yet")] IdoFuture, - #[msg("Not the correct time to invest")] + #[msg("Not the correct time to invest")] WrongInvestingTime, #[msg("Insufficient Fiat Tokens")] LowFiat @@ -839,9 +839,9 @@ describe("ido-program", () => { Now let’s write the first test ```ts - //--snip-- + //--snip-- - it("Can initialize the program state", async () => { + it("Can initialize the program state", async () => { const transferSig = await provider.connection.requestAirdrop( payer.publicKey, 10000000000 @@ -985,8 +985,9 @@ Now we are ready to test out our instructions. ### Initialize the Pool ```ts - //--snip-- - it("Can initialize the Pool", async () => { + //--snip-- + + it("Can initialize the Pool", async () => { const [_poolSigner, bump] = anchor.web3.PublicKey.findProgramAddressSync( [nativeMint.toBuffer()], @@ -1136,9 +1137,9 @@ Run `anchor test`. It should show 2 tests passed. ### Exchange Investor Fiat for Redeemable Tokens ```ts - //--snip-- + //--snip-- - let deposit = 5000; + let deposit = 5000; it("Can exchange investor Fiat for redeemable tokens", async () => { if (Date.now() < startIdoTs.toNumber() * 1000) { @@ -1213,8 +1214,8 @@ Running `anchor test` should now show 3 tests passed. ### Exchange User Redeemable For Native Tokens ```ts - //--snip-- - it("Can exchange investor Redeemable tokens for Native tokens", async () => { + //--snip-- + it("Can exchange investor Redeemable tokens for Native tokens", async () => { if (Date.now() < endIdoTs.toNumber() * 1000) { await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 5000); } @@ -1272,7 +1273,7 @@ Here also, we go through similar steps like the previous one. ### Withdraw Pool Fiat Tokens ```ts - it("Can withdraw total Fiat from pool account", async () => { + it("Can withdraw total Fiat from pool account", async () => { if (Date.now() < withDrawFiatTs.toNumber() * 1000) { await sleep(withDrawFiatTs.toNumber() * 1000 - Date.now() + 5000); } From 464f81ecb8d7ad8039d12c8cf76ca23154eed153 Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Sun, 2 Jul 2023 10:56:58 +0530 Subject: [PATCH 27/30] resolved merge conflicts --- programs/ido-program/programs/ido-program/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 8e2ed10..6702921 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -189,6 +189,8 @@ pub struct InitializePool<'info> { pub token_program: Program<'info, Token>, + + pub system_program: Program<'info, System>, } @@ -250,7 +252,7 @@ pub struct ExchangeRedeemableForNative<'info> { #[account( mut, - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + mint::authority = pool_signer )] pub redeemable_mint: Account<'info, Mint>, @@ -266,7 +268,6 @@ pub struct ExchangeRedeemableForNative<'info> { #[account(mut, constraint = investor_redeemable.owner == *authority.key)] pub investor_redeemable: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, } @@ -279,7 +280,7 @@ pub struct WithdrawPoolFiat<'info> { #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] pub pool_signer: AccountInfo<'info>, - #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] + #[account(adress = pool_fiat.mint)] pub fiat_mint: Account<'info, Mint>, #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] @@ -291,7 +292,6 @@ pub struct WithdrawPoolFiat<'info> { #[account(mut)] pub creator_fiat: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, } From f9f3a9e274d9ec595a3c5feb5ed4dc3092df1ac9 Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Sun, 2 Jul 2023 10:58:06 +0530 Subject: [PATCH 28/30] ran rustfmt --- programs/ido-program/programs/ido-program/src/lib.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 6702921..8807b76 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -189,8 +189,6 @@ pub struct InitializePool<'info> { pub token_program: Program<'info, Token>, - - pub system_program: Program<'info, System>, } @@ -392,9 +390,7 @@ fn unrestricted_phase<'info>(ctx: &Context>) -> } //iDO Over -fn ido_over<'info>( - pool_account: &Account<'info, PoolAccount>, -) -> Result<()> { +fn ido_over<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { if !(pool_account.end_ido_ts < Clock::get().unwrap().unix_timestamp) { return Err(ErrorCode::IdoNotOver.into()); } @@ -402,9 +398,7 @@ fn ido_over<'info>( } //Can Withdraw fiat -fn can_withdraw_fiat<'info>( - pool_account: &Account<'info, PoolAccount>, -) -> Result<()> { +fn can_withdraw_fiat<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { if !(pool_account.withdraw_fiat_ts < Clock::get().unwrap().unix_timestamp) { return Err(ErrorCode::CannotWithdrawYet.into()); } From 51b242df946f5a121c2aa5cf3f83d5d0da608733 Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Wed, 5 Jul 2023 00:09:50 +0530 Subject: [PATCH 29/30] made necessary changes to the programs according to review --- .../programs/ido-program/src/lib.rs | 167 +++++++++--------- programs/ido-program/tests/ido-program.ts | 164 +++++++++-------- 2 files changed, 160 insertions(+), 171 deletions(-) diff --git a/programs/ido-program/programs/ido-program/src/lib.rs b/programs/ido-program/programs/ido-program/src/lib.rs index 8807b76..e8c281c 100644 --- a/programs/ido-program/programs/ido-program/src/lib.rs +++ b/programs/ido-program/programs/ido-program/src/lib.rs @@ -1,5 +1,4 @@ -use anchor_lang::prelude::*; -use anchor_lang::solana_program::program_option::COption; +use anchor_lang::{prelude::*, solana_program::clock::UnixTimestamp}; use anchor_spl::token::{self, Burn, Mint, MintTo, Token, TokenAccount, Transfer}; declare_id!("GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6"); @@ -8,16 +7,16 @@ declare_id!("GYpxvUxtesyBSn69gnbfQChoUyJ7qdsG9nXS2Y2dQNH6"); pub mod ido_program { use super::*; - #[access_control(InitializePool::accounts(&ctx, bump) pre_ido_phase(start_ido_ts))] + #[access_control(pre_ido_phase(start_ido_ts))] pub fn initialize_pool( ctx: Context, total_native_tokens: u64, start_ido_ts: i64, end_ido_ts: i64, - withdraw_fiat_ts: i64, + withdraw_deposit_token_ts: i64, bump: u8, ) -> Result<()> { - if !(start_ido_ts < end_ido_ts && end_ido_ts < withdraw_fiat_ts) { + if !(start_ido_ts < end_ido_ts && end_ido_ts < withdraw_deposit_token_ts) { return Err(ErrorCode::NonSequentialTimestamps.into()); } @@ -30,13 +29,13 @@ pub mod ido_program { pool.pool_authority = *ctx.accounts.authority.key; pool.redeemable_mint = ctx.accounts.redeemable_mint.key(); pool.native_mint = ctx.accounts.native_mint.key(); - pool.fiat_mint = ctx.accounts.fiat_mint.key(); + pool.deposit_token_mint = ctx.accounts.deposit_token_mint.key(); pool.pool_native = ctx.accounts.pool_native.key(); - pool.pool_fiat = ctx.accounts.pool_fiat.key(); + pool.pool_deposit_token = ctx.accounts.pool_deposit_token.key(); pool.total_native_tokens = total_native_tokens; pool.start_ido_ts = start_ido_ts; pool.end_ido_ts = end_ido_ts; - pool.withdraw_fiat_ts = withdraw_fiat_ts; + pool.withdraw_deposit_token_ts = withdraw_deposit_token_ts; pool.bump = bump; @@ -55,29 +54,29 @@ pub mod ido_program { } #[access_control(unrestricted_phase(&ctx))] - pub fn exchange_fiat_for_redeemable( - ctx: Context, + pub fn exchange_deposit_token_for_redeemable( + ctx: Context, amount: u64, ) -> Result<()> { if amount == 0 { return Err(ErrorCode::InvalidParameter.into()); } // While token::transfer will check this, we prefer a verbose error msg - if ctx.accounts.investor_fiat.amount < amount { - return Err(ErrorCode::LowFiat.into()); + if ctx.accounts.depositor_deposit_token.amount < amount { + return Err(ErrorCode::LowDepositToken.into()); } - // Transfer investor's fiat to pool fiat account. + // Transfer depositor's deposit_token to pool deposit_token account. let cpi_accounts = Transfer { - from: ctx.accounts.investor_fiat.to_account_info(), - to: ctx.accounts.pool_fiat.to_account_info(), + from: ctx.accounts.depositor_deposit_token.to_account_info(), + to: ctx.accounts.pool_deposit_token.to_account_info(), authority: ctx.accounts.authority.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, amount)?; - // Mint Redeemable to investor Redeemable account. + // Mint Redeemable to depositor Redeemable account. let seeds = &[ ctx.accounts.pool.native_mint.as_ref(), &[ctx.accounts.pool.bump], @@ -85,7 +84,7 @@ pub mod ido_program { let signer = &[&seeds[..]]; let cpi_accounts = MintTo { mint: ctx.accounts.redeemable_mint.to_account_info(), - to: ctx.accounts.investor_redeemable.to_account_info(), + to: ctx.accounts.depositor_redeemable.to_account_info(), authority: ctx.accounts.pool_signer.clone(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); @@ -97,7 +96,7 @@ pub mod ido_program { #[access_control(ido_over(&ctx.accounts.pool))] pub fn exchange_redeemable_for_native(ctx: Context) -> Result<()> { - let native_amount = (ctx.accounts.investor_redeemable.amount as u128) + let native_amount = (ctx.accounts.depositor_redeemable.amount as u128) .checked_mul(ctx.accounts.pool_native.amount as u128) .unwrap() .checked_div(ctx.accounts.redeemable_mint.supply as u128) @@ -105,14 +104,14 @@ pub mod ido_program { let cpi_accounts = Burn { mint: ctx.accounts.redeemable_mint.to_account_info(), - from: ctx.accounts.investor_redeemable.to_account_info(), + from: ctx.accounts.depositor_redeemable.to_account_info(), authority: ctx.accounts.authority.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - token::burn(cpi_ctx, ctx.accounts.investor_redeemable.amount)?; + token::burn(cpi_ctx, ctx.accounts.depositor_redeemable.amount)?; let seeds = &[ ctx.accounts.pool.native_mint.as_ref(), @@ -122,7 +121,7 @@ pub mod ido_program { let cpi_accounts = Transfer { from: ctx.accounts.pool_native.to_account_info(), - to: ctx.accounts.investor_native.to_account_info(), + to: ctx.accounts.depositor_native.to_account_info(), authority: ctx.accounts.pool_signer.to_account_info(), }; @@ -134,21 +133,21 @@ pub mod ido_program { Ok(()) } - #[access_control(can_withdraw_fiat(&ctx.accounts.pool))] - pub fn withdraw_pool_fiat(ctx: Context) -> Result<()> { + #[access_control(can_withdraw_deposit_token(&ctx.accounts.pool))] + pub fn withdraw_pool_deposit_token(ctx: Context) -> Result<()> { let seeds = &[ ctx.accounts.pool.native_mint.as_ref(), &[ctx.accounts.pool.bump], ]; let signer = &[&seeds[..]]; let cpi_accounts = Transfer { - from: ctx.accounts.pool_fiat.to_account_info(), - to: ctx.accounts.creator_fiat.to_account_info(), + from: ctx.accounts.pool_deposit_token.to_account_info(), + to: ctx.accounts.creator_deposit_token.to_account_info(), authority: ctx.accounts.pool_signer.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; + token::transfer(cpi_ctx, ctx.accounts.pool_deposit_token.amount)?; Ok(()) } @@ -160,7 +159,11 @@ pub struct InitializePool<'info> { pub pool: Box>, /// CHECK: This is not dangerous - #[account(mut)] + #[account( + mut, + seeds = [native_mint.key().as_ref()], + bump + )] pub pool_signer: AccountInfo<'info>, #[account( @@ -170,7 +173,7 @@ pub struct InitializePool<'info> { pub redeemable_mint: Box>, #[account(mint::decimals = redeemable_mint.decimals)] - pub fiat_mint: Box>, + pub deposit_token_mint: Box>, #[account(address = pool_native.mint)] pub native_mint: Box>, @@ -178,8 +181,8 @@ pub struct InitializePool<'info> { #[account(mut, constraint = pool_native.owner == *pool_signer.key)] pub pool_native: Box>, - #[account(constraint = pool_fiat.owner == *pool_signer.key)] - pub pool_fiat: Box>, + #[account(constraint = pool_deposit_token.owner == *pool_signer.key)] + pub pool_deposit_token: Box>, #[account(mut)] pub authority: Signer<'info>, @@ -192,23 +195,9 @@ pub struct InitializePool<'info> { pub system_program: Program<'info, System>, } -impl<'info> InitializePool<'info> { - fn accounts(ctx: &Context>, bump: u8) -> Result<()> { - let expected_signer = Pubkey::create_program_address( - &[ctx.accounts.pool_native.mint.as_ref(), &[bump]], - ctx.program_id, - ) - .map_err(|_| ErrorCode::InvalidBump)?; - if ctx.accounts.pool_signer.key != &expected_signer { - return Err(ErrorCode::InvalidBump.into()); - } - Ok(()) - } -} - #[derive(Accounts)] -pub struct ExchangeFiatForRedeemable<'info> { - #[account(mut, has_one = redeemable_mint, has_one = pool_fiat)] +pub struct ExchangeDepositTokenForRedeemable<'info> { + #[account(mut, has_one = redeemable_mint, has_one = pool_deposit_token)] pub pool: Box>, ///CHECK: This is not dangerous @@ -221,20 +210,20 @@ pub struct ExchangeFiatForRedeemable<'info> { )] pub redeemable_mint: Account<'info, Mint>, - #[account(mut, address = pool_fiat.mint)] - pub fiat_mint: Account<'info, Mint>, + #[account(mut, address = pool_deposit_token.mint)] + pub deposit_token_mint: Account<'info, Mint>, - #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] - pub pool_fiat: Account<'info, TokenAccount>, + #[account(mut, constraint = pool_deposit_token.owner == *pool_signer.key)] + pub pool_deposit_token: Account<'info, TokenAccount>, #[account(mut)] pub authority: Signer<'info>, - #[account(mut, constraint = investor_fiat.owner == *authority.key)] - pub investor_fiat: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_deposit_token.owner == *authority.key)] + pub depositor_deposit_token: Account<'info, TokenAccount>, - #[account(mut, constraint = investor_redeemable.owner == *authority.key)] - pub investor_redeemable: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_redeemable.owner == *authority.key)] + pub depositor_redeemable: Account<'info, TokenAccount>, pub token_program: Program<'info, Token>, } @@ -260,35 +249,35 @@ pub struct ExchangeRedeemableForNative<'info> { #[account(mut)] pub authority: Signer<'info>, - #[account(mut, constraint = investor_native.owner == *authority.key)] - pub investor_native: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_native.owner == *authority.key)] + pub depositor_native: Account<'info, TokenAccount>, - #[account(mut, constraint = investor_redeemable.owner == *authority.key)] - pub investor_redeemable: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_redeemable.owner == *authority.key)] + pub depositor_redeemable: Account<'info, TokenAccount>, pub token_program: Program<'info, Token>, } #[derive(Accounts)] -pub struct WithdrawPoolFiat<'info> { - #[account(has_one = pool_fiat)] +pub struct WithdrawPoolDepositToken<'info> { + #[account(has_one = pool_deposit_token)] pub pool: Box>, ///CHECK: This is not dangerous #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] pub pool_signer: AccountInfo<'info>, - #[account(adress = pool_fiat.mint)] - pub fiat_mint: Account<'info, Mint>, + #[account(address = pool_deposit_token.mint)] + pub deposit_token_mint: Account<'info, Mint>, - #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] - pub pool_fiat: Account<'info, TokenAccount>, + #[account(mut, constraint = pool_deposit_token.owner == *pool_signer.key)] + pub pool_deposit_token: Account<'info, TokenAccount>, #[account(mut)] pub payer: Signer<'info>, #[account(mut)] - pub creator_fiat: Account<'info, TokenAccount>, + pub creator_deposit_token: Account<'info, TokenAccount>, pub token_program: Program<'info, Token>, } @@ -304,14 +293,14 @@ pub struct PoolAccount { /// Mint of project tokens pub native_mint: Pubkey, - /// Mint of fiat tokens - pub fiat_mint: Pubkey, + /// Mint of deposit_token tokens + pub deposit_token_mint: Pubkey, /// Token Account of Pool associated with the project token mint pub pool_native: Pubkey, - /// Token Account of Pool associated with fiat mint - pub pool_fiat: Pubkey, + /// Token Account of Pool associated with deposit_token mint + pub pool_deposit_token: Pubkey, /// Total number of native tokens being distributed pub total_native_tokens: u64, @@ -322,8 +311,8 @@ pub struct PoolAccount { /// Unix timestamp for ending IDO pub end_ido_ts: i64, - /// Unix timestamp for withdrawing Fiat from pool - pub withdraw_fiat_ts: i64, + /// Unix timestamp for withdrawing deposit_token from pool + pub withdraw_deposit_token_ts: i64, /// Bump pub bump: u8, @@ -333,14 +322,14 @@ impl PoolAccount { pub const LEN: usize = DISCRIMINATOR_LENGTH // Discriminator Length + PUBKEY_LENGTH // Pool Authority + PUBKEY_LENGTH // Redeemable Mint - + PUBKEY_LENGTH // Fiat Mint + + PUBKEY_LENGTH // deposit_token Mint + PUBKEY_LENGTH // Pool Native Token Account + PUBKEY_LENGTH // Native Mint - + PUBKEY_LENGTH // Pool fiat Token Account + + PUBKEY_LENGTH // Pool deposit_token Token Account + DATA_LENGTH_64 // Total Native Token Amount + DATA_LENGTH_64 // Start IDO TS + DATA_LENGTH_64 // End IDO TS - + DATA_LENGTH_64 // Withdraw Fiat TS + + DATA_LENGTH_64 // Withdraw deposit_token TS + DATA_LENGTH_8; // Bump } @@ -355,17 +344,15 @@ pub enum ErrorCode { NonSequentialTimestamps, #[msg("Invalid Parameter")] InvalidParameter, - #[msg("Invalid Bump")] - InvalidBump, #[msg("IDO has not begun yet")] IdoFuture, #[msg("Not the correct time to invest")] WrongInvestingTime, - #[msg("Insufficient Fiat Tokens")] - LowFiat, + #[msg("Insufficient deposit_token Tokens")] + LowDepositToken, #[msg("IDO has not ended yet")] IdoNotOver, - #[msg("Cannot withdraw Fiat yet")] + #[msg("Cannot withdraw deposit_token yet")] CannotWithdrawYet, } @@ -373,16 +360,18 @@ pub enum ErrorCode { // IDO Starts in the Future fn pre_ido_phase<'info>(start_ido_ts: i64) -> Result<()> { - if !(Clock::get().unwrap().unix_timestamp < start_ido_ts) { + if !(get_timestamp() < start_ido_ts) { return Err(ErrorCode::IdoFuture.into()); } Ok(()) } // Unrestricted Phase -fn unrestricted_phase<'info>(ctx: &Context>) -> Result<()> { - if !(ctx.accounts.pool.start_ido_ts < Clock::get().unwrap().unix_timestamp - && ctx.accounts.pool.end_ido_ts > Clock::get().unwrap().unix_timestamp) +fn unrestricted_phase<'info>( + ctx: &Context>, +) -> Result<()> { + if !(ctx.accounts.pool.start_ido_ts < get_timestamp() + && ctx.accounts.pool.end_ido_ts > get_timestamp()) { return Err(ErrorCode::WrongInvestingTime.into()); } @@ -391,16 +380,20 @@ fn unrestricted_phase<'info>(ctx: &Context>) -> //iDO Over fn ido_over<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { - if !(pool_account.end_ido_ts < Clock::get().unwrap().unix_timestamp) { + if !(pool_account.end_ido_ts < get_timestamp()) { return Err(ErrorCode::IdoNotOver.into()); } Ok(()) } -//Can Withdraw fiat -fn can_withdraw_fiat<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { - if !(pool_account.withdraw_fiat_ts < Clock::get().unwrap().unix_timestamp) { +//Can Withdraw deposit_token +fn can_withdraw_deposit_token<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { + if !(pool_account.withdraw_deposit_token_ts < get_timestamp()) { return Err(ErrorCode::CannotWithdrawYet.into()); } Ok(()) } + +pub fn get_timestamp() -> UnixTimestamp { + Clock::get().unwrap().unix_timestamp +} diff --git a/programs/ido-program/tests/ido-program.ts b/programs/ido-program/tests/ido-program.ts index ef4f369..993fd05 100644 --- a/programs/ido-program/tests/ido-program.ts +++ b/programs/ido-program/tests/ido-program.ts @@ -21,32 +21,32 @@ describe("ido-program", () => { let nativeTokenAmount = new anchor.BN(1000000); - let fiatMint: PublicKey; + let depositTokenMint: PublicKey; let redeemableMint: PublicKey; let nativeMint: PublicKey; - let projectFiat: PublicKey; - let projectNative: PublicKey; + let projectDepositAccount: PublicKey; + let projectNativeAccount: PublicKey; - let investorFiat: PublicKey; - let investorNative: PublicKey; - let investorRedeemable: PublicKey; + let depositorDepositAccount: PublicKey; + let depositorNativeAccount: PublicKey; + let depositorRedeemable: PublicKey; let poolNative: PublicKey; - let poolFiat: PublicKey; + let poolDepositToken: PublicKey; let poolSigner: PublicKey; let nowBn: anchor.BN; let startIdoTs: anchor.BN; let endIdoTs: anchor.BN; - let withDrawFiatTs: anchor.BN; + let withDrawDepositTokenTs: anchor.BN; const payer = anchor.web3.Keypair.generate(); const mintAuthority = anchor.web3.Keypair.generate(); const project = anchor.web3.Keypair.generate(); - const investor = anchor.web3.Keypair.generate(); + const depositor = anchor.web3.Keypair.generate(); let pool = anchor.web3.Keypair.generate(); @@ -79,14 +79,14 @@ describe("ido-program", () => { }), SystemProgram.transfer({ fromPubkey: payer.publicKey, - toPubkey: investor.publicKey, + toPubkey: depositor.publicKey, lamports: 2000000000, }) ); await provider.sendAndConfirm(tx, [payer]); - fiatMint = await createMint( + depositTokenMint = await createMint( provider.connection, payer, mintAuthority.publicKey, @@ -102,39 +102,39 @@ describe("ido-program", () => { 0 ); - projectFiat = await createAccount( + projectDepositAccount = await createAccount( provider.connection, payer, - fiatMint, + depositTokenMint, project.publicKey ); - projectNative = await createAccount( + projectNativeAccount = await createAccount( provider.connection, payer, nativeMint, project.publicKey ); - investorFiat = await createAccount( + depositorDepositAccount = await createAccount( provider.connection, payer, - fiatMint, - investor.publicKey + depositTokenMint, + depositor.publicKey ); - investorNative = await createAccount( + depositorNativeAccount = await createAccount( provider.connection, payer, nativeMint, - investor.publicKey + depositor.publicKey ); await mintTo( provider.connection, payer, nativeMint, - projectNative, + projectNativeAccount, mintAuthority, nativeTokenAmount.toNumber() ); @@ -142,19 +142,19 @@ describe("ido-program", () => { await mintTo( provider.connection, payer, - fiatMint, - investorFiat, + depositTokenMint, + depositorDepositAccount, mintAuthority, 10000 ); - const projectNativeTokenAccount = await getAccount( + const projectNativeAccountTokenAccount = await getAccount( provider.connection, - projectNative + projectNativeAccount ); assert.strictEqual( - projectNativeTokenAccount.amount.toString(), + projectNativeAccountTokenAccount.amount.toString(), nativeTokenAmount.toNumber().toString() ); }); @@ -176,11 +176,11 @@ describe("ido-program", () => { 0 ); - investorRedeemable = await createAccount( + depositorRedeemable = await createAccount( provider.connection, payer, redeemableMint, - investor.publicKey + depositor.publicKey ); let poolNativeAccount = await getOrCreateAssociatedTokenAccount( @@ -194,42 +194,41 @@ describe("ido-program", () => { poolNative = poolNativeAccount.address; - let poolFiatAccount = await getOrCreateAssociatedTokenAccount( + let poolDepositTokenAccount = await getOrCreateAssociatedTokenAccount( provider.connection, payer, - fiatMint, + depositTokenMint, poolSigner, true, undefined ); - poolFiat = poolFiatAccount.address; + poolDepositToken = poolDepositTokenAccount.address; nowBn = new anchor.BN(Date.now() / 1000); startIdoTs = nowBn.add(new anchor.BN(10)); endIdoTs = nowBn.add(new anchor.BN(20)); - withDrawFiatTs = nowBn.add(new anchor.BN(30)); + withDrawDepositTokenTs = nowBn.add(new anchor.BN(30)); await program.methods .initializePool( nativeTokenAmount, startIdoTs, endIdoTs, - withDrawFiatTs, + withDrawDepositTokenTs, bump ) .accounts({ pool: pool.publicKey, poolSigner: poolSigner, redeemableMint: redeemableMint, - fiatMint: fiatMint, + depositTokenMint: depositTokenMint, nativeMint: nativeMint, poolNative: poolNative, - poolFiat: poolFiat, + poolDepositToken: poolDepositToken, authority: project.publicKey, - creatorNative: projectNative, + creatorNative: projectNativeAccount, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, systemProgram: anchor.web3.SystemProgram.programId, }) .signers([pool, project]) @@ -266,8 +265,8 @@ describe("ido-program", () => { nativeMint.toBase58() ); assert.strictEqual( - createdPool.poolFiat.toBase58(), - poolFiat.toBase58() + createdPool.poolDepositToken.toBase58(), + poolDepositToken.toBase58() ); assert.strictEqual( createdPool.totalNativeTokens.toNumber().toString(), @@ -282,67 +281,66 @@ describe("ido-program", () => { endIdoTs.toString() ); assert.strictEqual( - createdPool.withdrawFiatTs.toNumber().toString(), - withDrawFiatTs.toString() + createdPool.withdrawDepositTokenTs.toNumber().toString(), + withDrawDepositTokenTs.toString() ); }); let firstDeposit = 5000; - it("Can exchange investor Fiat for redeemable tokens", async () => { + it("Can exchange depositor Deposit tokens for Redeemable tokens", async () => { if (Date.now() < startIdoTs.toNumber() * 1000) { await sleep(startIdoTs.toNumber() * 1000 - Date.now() + 5000); } await program.methods - .exchangeFiatForRedeemable(new anchor.BN(firstDeposit)) + .exchangeDepositTokenForRedeemable(new anchor.BN(firstDeposit)) .accounts({ pool: pool.publicKey, poolSigner: poolSigner, redeemableMint: redeemableMint, - fiatMint: fiatMint, - poolFiat: poolFiat, - authority: investor.publicKey, - investorFiat: investorFiat, - investorRedeemable: investorRedeemable, + depositTokenMint: depositTokenMint, + poolDepositToken: poolDepositToken, + authority: depositor.publicKey, + depositorDepositToken: depositorDepositAccount, + depositorRedeemable: depositorRedeemable, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }) - .signers([investor]) + .signers([depositor]) .rpc(); - const poolFiatTokenAccount = await getAccount( + const poolDepositTokenTokenAccount = await getAccount( provider.connection, - poolFiat + poolDepositToken ); assert.strictEqual( - poolFiatTokenAccount.amount.toString(), + poolDepositTokenTokenAccount.amount.toString(), firstDeposit.toString() ); - const investorFiatTokenAccount = await getAccount( + const depositorDepositAccountTokenAccount = await getAccount( provider.connection, - investorFiat + depositorDepositAccount ); - const investorRedeemableTokenAccount = await getAccount( + const depositorRedeemableTokenAccount = await getAccount( provider.connection, - investorRedeemable + depositorRedeemable ); assert.strictEqual( - investorFiatTokenAccount.amount.toString(), + depositorDepositAccountTokenAccount.amount.toString(), (10000 - firstDeposit).toString() ); assert.strictEqual( - investorRedeemableTokenAccount.amount.toString(), + depositorRedeemableTokenAccount.amount.toString(), firstDeposit.toString() ); }); - it("Can exchange investor Redeemable tokens for Native tokens", async () => { + it("Can exchange depositor Redeemable tokens for Native tokens", async () => { if (Date.now() < endIdoTs.toNumber() * 1000) { await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 5000); } @@ -354,13 +352,12 @@ describe("ido-program", () => { poolSigner: poolSigner, redeemableMint: redeemableMint, poolNative: poolNative, - authority: investor.publicKey, - investorNative: investorNative, - investorRedeemable: investorRedeemable, + authority: depositor.publicKey, + depositorNative: depositorNativeAccount, + depositorRedeemable: depositorRedeemable, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }) - .signers([investor]) + .signers([depositor]) .rpc(); const poolNativeTokenAccount = await getAccount( @@ -370,61 +367,60 @@ describe("ido-program", () => { assert.strictEqual(poolNativeTokenAccount.amount.toString(), "0"); - const investorNativeTokenAccount = await getAccount( + const depositorNativeAccountTokenAccount = await getAccount( provider.connection, - investorNative + depositorNativeAccount ); - const investorRedeemableTokenAccount = await getAccount( + const depositorRedeemableTokenAccount = await getAccount( provider.connection, - investorRedeemable + depositorRedeemable ); assert.strictEqual( - investorNativeTokenAccount.amount.toString(), + depositorNativeAccountTokenAccount.amount.toString(), nativeTokenAmount.toString() ); assert.strictEqual( - investorRedeemableTokenAccount.amount.toString(), + depositorRedeemableTokenAccount.amount.toString(), "0" ); }); - it("Can withdraw total Fiat from pool account", async () => { - if (Date.now() < withDrawFiatTs.toNumber() * 1000) { - await sleep(withDrawFiatTs.toNumber() * 1000 - Date.now() + 5000); + it("Can withdraw total deposit tokens from pool account", async () => { + if (Date.now() < withDrawDepositTokenTs.toNumber() * 1000) { + await sleep(withDrawDepositTokenTs.toNumber() * 1000 - Date.now() + 5000); } await program.methods - .withdrawPoolFiat() + .withdrawPoolDepositToken() .accounts({ pool: pool.publicKey, poolSigner: poolSigner, - fiatMint: fiatMint, - poolFiat: poolFiat, + depositTokenMint: depositTokenMint, + poolDepositToken: poolDepositToken, payer: project.publicKey, - creatorFiat: projectFiat, + creatorDepositToken: projectDepositAccount, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }) .signers([project]) .rpc(); - const poolFiatTokenAccount = await getAccount( + const poolDepositTokenTokenAccount = await getAccount( provider.connection, - poolFiat + poolDepositToken ); - assert.strictEqual(poolFiatTokenAccount.amount.toString(), "0"); + assert.strictEqual(poolDepositTokenTokenAccount.amount.toString(), "0"); - const projectFiatTokenAccount = await getAccount( + const projectDepositAccountTokenAccount = await getAccount( provider.connection, - projectFiat + projectDepositAccount ); assert.strictEqual( - projectFiatTokenAccount.amount.toString(), + projectDepositAccountTokenAccount.amount.toString(), firstDeposit.toString() ); }); From e1345adc770fc812d397fb80faab38b12e5c1acb Mon Sep 17 00:00:00 2001 From: 0xcatrovacer Date: Wed, 5 Jul 2023 00:10:11 +0530 Subject: [PATCH 30/30] made necessary changes to the content --- .../milestone_project_ido-program.md | 569 ++++++++---------- 1 file changed, 253 insertions(+), 316 deletions(-) diff --git a/src/anchor_in_depth/milestone_project_ido-program.md b/src/anchor_in_depth/milestone_project_ido-program.md index 743efae..62043ee 100644 --- a/src/anchor_in_depth/milestone_project_ido-program.md +++ b/src/anchor_in_depth/milestone_project_ido-program.md @@ -8,9 +8,9 @@ It is now time to implement the in-depth concepts you learned in section 4 of th anchor init ido-program ``` -As the name suggests, this is a program that helps projects do an Initial DEX Offering to investors. +As the name suggests, this is a program that helps projects do an Initial DEX Offering to depositors. -This program will be doing a couple of things. In an IDO, a project distributes its native tokens among willing investors. There are a few types of IDOs which are done. We will be implementing a fair launch IDO platform. In here, a project escrows the native tokens into a pool. When the IDO opens, investors come and deposit their fiat tokens into the pool. After the IDO is over, investors can claim their native tokens, the amount of which is calculated by the the pool percentage share of their deposit. The project can also finally withdraw all the deposited fiat tokens. +This program will be doing a couple of things. In an IDO, a project distributes its native tokens among willing depositors. There are a few types of IDOs which are done. We will be implementing a fair launch IDO platform. In here, a project escrows the native tokens into a pool. When the IDO opens, depositors come and deposit their deposit tokens into the pool. After the IDO is over, depositors can claim their native tokens, the amount of which is calculated by the the pool percentage share of their deposit. The project can also finally withdraw all the deposited deposit tokens. We recommend keeping programs in a single `lib.rs` file. @@ -18,7 +18,7 @@ We recommend keeping programs in a single `lib.rs` file. Let’s think about what information we would want to store in our pool account. We would want to know the authority of the pool. -The mint public keys of the redeemable intermediary token, native tokens, and fiat tokens are also necessary to ensure that the correct tokens are deposited in the pool. +The mint public keys of the redeemable intermediary token, native tokens, and deposit tokens are also necessary to ensure that the correct tokens are deposited in the pool. We would need to know the associated token accounts of the pool authority which would store and distribute tokens. Additionally, we would need to know the number of tokens being distributed. Finally, we would require the timestamps for the IDO and the bump associated with the pool PDA. Add the following code at the bottom of the `lib.rs` file @@ -35,14 +35,14 @@ pub struct PoolAccount { /// Mint of project tokens pub native_mint: Pubkey, - /// Mint of fiat tokens - pub fiat_mint: Pubkey, + /// Mint of deposit tokens + pub deposit token_mint: Pubkey, /// Token Account of Pool associated with the project token mint pub pool_native: Pubkey, - /// Token Account of Pool associated with fiat mint - pub pool_fiat: Pubkey, + /// Token Account of Pool associated with deposit token mint + pub pool_deposit token: Pubkey, /// Total number of native tokens being distributed pub total_native_tokens: u64, @@ -53,8 +53,8 @@ pub struct PoolAccount { /// Unix timestamp for ending IDO pub end_ido_ts: i64, - /// Unix timestamp for withdrawing fiat tokens from the pool - pub withdraw_fiat_ts: i64, + /// Unix timestamp for withdrawing deposit token from pool + pub withdraw_deposit token_ts: i64, /// Bump pub bump: u8, @@ -64,15 +64,15 @@ impl PoolAccount { pub const LEN: usize = DISCRIMINATOR_LENGTH // Discriminator Length + PUBKEY_LENGTH // Pool Authority + PUBKEY_LENGTH // Redeemable Mint - + PUBKEY_LENGTH // Fiat Mint + + PUBKEY_LENGTH // deposit token Mint + PUBKEY_LENGTH // Pool Native Token Account + PUBKEY_LENGTH // Native Mint - + PUBKEY_LENGTH // Pool fiat Token Account + + PUBKEY_LENGTH // Pool deposit token Account + DATA_LENGTH_64 // Total Native Token Amount + DATA_LENGTH_64 // Start IDO TS + DATA_LENGTH_64 // End IDO TS - + DATA_LENGTH_64 // Withdraw Native TS - + DATA_LENGTH_8; // Bump + + DATA_LENGTH_64 // Withdraw deposit token TS + + DATA_LENGTH_8; // Bump } const DISCRIMINATOR_LENGTH: usize = 8; @@ -85,7 +85,7 @@ This is all the data we store in `PoolAccount`. We also add the `impl` block for ## Instructions and Handler Functions -Let’s think about our program flow. The program will have 4 instructions. We should be able to initialize a pool that will hold information regarding the IDO. When the IDO starts, investors should be able to exchange their fiat tokens for the same number of intermediary tokens. After the IDO is over, investors should be able to exchange their intermediary tokens for the project’s tokens. Finally, the project should be able to redeem all the fiat tokens from the pool. +Let’s think about our program flow. The program will have 4 instructions. We should be able to initialize a pool that will hold information regarding the IDO. When the IDO starts, depositors should be able to exchange their deposit tokens for the same number of intermediary tokens. After the IDO is over, depositors should be able to exchange their intermediary tokens for the project’s tokens. Finally, the project should be able to redeem all the deposit tokens from the pool. ### Initializing the Pool @@ -98,26 +98,30 @@ pub struct InitializePool<'info> { pub pool: Box>, /// CHECK: This is not dangerous - #[account(mut)] + #[account( + mut, + seeds = [native_mint.key().as_ref()], + bump + )] pub pool_signer: AccountInfo<'info>, #[account( - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key), + mint::authority = pool_signer, constraint = redeemable_mint.supply == 0 )] pub redeemable_mint: Box>, - #[account(constraint = fiat_mint.decimals == redeemable_mint.decimals)] - pub fiat_mint: Box>, + #[account(mint::decimals = redeemable_mint.decimals)] + pub deposit_token_mint: Box>, - #[account(constraint = pool_native.mint == *native_mint.to_account_info().key)] + #[account(address = pool_native.mint)] pub native_mint: Box>, #[account(mut, constraint = pool_native.owner == *pool_signer.key)] pub pool_native: Box>, - #[account(constraint = pool_fiat.owner == *pool_signer.key)] - pub pool_fiat: Box>, + #[account(constraint = pool_deposit_token.owner == *pool_signer.key)] + pub pool_deposit_token: Box>, #[account(mut)] pub authority: Signer<'info>, @@ -127,10 +131,6 @@ pub struct InitializePool<'info> { pub token_program: Program<'info, Token>, - pub rent: Sysvar<'info, Rent>, - - pub clock: Sysvar<'info, Clock>, - pub system_program: Program<'info, System>, } ``` @@ -140,10 +140,10 @@ Let’s look at the accounts we added - `pool`: We create the pool account with the init constraint. We add a payer by the name `authority`: which we add later in the account struct. And we also add the space needed to store the account which we already calculated in the `impl` block of `PoolAccount`. - `pool_signer`: This is the authority that will have control of the pool. - `redeemable_mint`: The mint address of the redeemable tokens. We add some constraints to check that the token has the correct configurations set. -- `fiat_mint`: The mint address of the fiat tokens. We use the constraint that the decimals of the fiat tokens and the redeemable tokens should be the same. +- `deposit token_mint`: The mint address of the deposit tokens. We use the constraint that the decimals of the deposit tokens and the redeemable tokens should be the same. - `native_mint`: The mint address of the native token. - `pool_native`: The token account owned by the pool signer that will hold all the native tokens. -- `pool_fiat`: The token account owned by the pool signer that will hold all the fiat tokens sent by the investor. +- `pool_deposit_token`: The token account owned by the pool signer that will hold all the deposit tokens sent by the depositor. - `authority`: The transaction signer - `creator_native`: The token account owned by the project which will send the native tokens to `pool_native` @@ -201,19 +201,16 @@ Now we can move forward with writing the handler function `initialize_pool` pub mod ido_program { use super::*; - #[access_control(InitializePool::accounts(&ctx, bump) pre_ido_phase(&ctx, start_ido_ts))] + #[access_control(pre_ido_phase(start_ido_ts))] pub fn initialize_pool( ctx: Context, total_native_tokens: u64, start_ido_ts: i64, end_ido_ts: i64, - withdraw_fiat_ts: i64, + withdraw_deposit_token_ts: i64, bump: u8, ) -> Result<()> { - - if !(start_ido_ts < end_ido_ts - && end_ido_ts < withdraw_fiat_ts) - { + if !(start_ido_ts < end_ido_ts && end_ido_ts < withdraw_deposit_token_ts) { return Err(ErrorCode::NonSequentialTimestamps.into()); } @@ -226,13 +223,13 @@ pub mod ido_program { pool.pool_authority = *ctx.accounts.authority.key; pool.redeemable_mint = ctx.accounts.redeemable_mint.key(); pool.native_mint = ctx.accounts.native_mint.key(); - pool.fiat_mint = ctx.accounts.fiat_mint.key(); + pool.deposit_token_mint = ctx.accounts.deposit_token_mint.key(); pool.pool_native = ctx.accounts.pool_native.key(); - pool.pool_fiat = ctx.accounts.pool_fiat.key(); + pool.pool_deposit_token = ctx.accounts.pool_deposit_token.key(); pool.total_native_tokens = total_native_tokens; pool.start_ido_ts = start_ido_ts; pool.end_ido_ts = end_ido_ts; - pool.withdraw_fiat_ts = withdraw_fiat_ts; + pool.withdraw_deposit_token_ts = withdraw_deposit_token_ts; pool.bump = bump; @@ -243,7 +240,7 @@ pub mod ido_program { authority: ctx.accounts.authority.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, total_native_tokens)?; @@ -264,37 +261,9 @@ We create a new `CpiContext` with the token program and the `Transfer` accounts And finally, we make a call to `token::transfer()` with the context and the number of tokens specified in the parameters. -The code will not compile yet. This is because the function `pre_ido_phase()` we called under `access_control` and the custom `ErrorCode` s we wrote have not been defined yet. There is also no `accounts()` function under the `InitializePool` implementation block. - -Add this after the `InitializePool` account structure. - -```rust - //--snip-- - pub clock: Sysvar<'info, Clock>, - - pub system_program: Program<'info, System>, -} - -impl<'info> InitializePool<'info> { - fn accounts(ctx: &Context>, bump: u8) -> Result<()> { - let expected_signer = Pubkey::create_program_address( - &[ctx.accounts.pool_native.mint.as_ref(), &[bump]], - ctx.program_id, - ) - .map_err(|_| ErrorCode::InvalidBump)?; - if ctx.accounts.pool_signer.key != &expected_signer { - return Err(ErrorCode::InvalidBump.into()); - } - Ok(()) - } -} - -//--snip-- -``` +The code will not compile yet. This is because the function `pre_ido_phase()` we called under `access_control` and the custom `ErrorCode`s we wrote have not been defined yet. -The `accounts()` function here is checking if the `pool_signer` PDA passed in the accounts structure of `InitializePool` has the correct seeds or not. - -And finally, add this at the bottom of the file. +Add this at the bottom of the file. ```rust //--snip-- @@ -305,8 +274,6 @@ pub enum ErrorCode { NonSequentialTimestamps, #[msg("Invalid Parameter")] InvalidParameter, - #[msg("Invalid Bump")] - InvalidBump, #[msg("IDO has not begun yet")] IdoFuture } @@ -315,106 +282,113 @@ pub enum ErrorCode { // Access Control Modifiers // IDO Starts in the Future -fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) -> Result<()> { - if !(ctx.accounts.clock.unix_timestamp < start_ido_ts) { +fn pre_ido_phase<'info>(start_ido_ts: i64) -> Result<()> { + if !(get_timestamp() < start_ido_ts) { return Err(ErrorCode::IdoFuture.into()); } Ok(()) } ``` +And finally we add in the `get_timestamp()` function. Add this after the `pre_ido_phase()` function definition. + +```rust +pub fn get_timestamp() -> UnixTimestamp { + Clock::get().unwrap().unix_timestamp +} +``` + Inside the `pre_ido_phase` function, we must ensure that the `start_ido_ts` timestamp parameter is greater than the current timestamp. Now, run `anchor build`. Now everything will compile correctly, with a warning of unused imports, which we can ignore. -### Exchanging Investor’s Fiat for Redeemable Tokens +### Exchanging Depositor’s Deposit Token for Redeemable Tokens -Now we can add the instruction that will allow investors to deposit their fiat tokens and receive an equal number of redeemable tokens. We will write the handler function `exchange_fiat_for_redeemable()` and the `ExchangeFiatForRedeemable` +Now we can add the instruction that will allow depositors to deposit their deposit tokens and receive an equal number of redeemable tokens. We will write the handler function `exchange_deposit_token_for_redeemable()` and the `ExchangeDepositTokenForRedeemable` accounts struct. -Let’s start with `ExchangeFiatForRedeemable`. Add this code after the impl block of `InitializePool` +Let’s start with `ExchangeDepositTokenForRedeemable`. Add this code after the `InitializePool` accounts struct. ```rust #[derive(Accounts)] -pub struct ExchangeFiatForRedeemable<'info> { - #[account(mut, has_one = redeemable_mint, has_one = pool_fiat)] +pub struct ExchangeDepositTokenForRedeemable<'info> { + #[account(mut, has_one = redeemable_mint, has_one = pool_deposit_token)] pub pool: Box>, ///CHECK: This is not dangerous #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] - pool_signer: AccountInfo<'info>, + pub pool_signer: AccountInfo<'info>, #[account( mut, - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + mint::authority = pool_signer )] pub redeemable_mint: Account<'info, Mint>, - #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] - pub fiat_mint: Account<'info, Mint>, + #[account(mut, address = pool_deposit_token.mint)] + pub deposit_token_mint: Account<'info, Mint>, - #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] - pub pool_fiat: Account<'info, TokenAccount>, + #[account(mut, constraint = pool_deposit_token.owner == *pool_signer.key)] + pub pool_deposit_token: Account<'info, TokenAccount>, #[account(mut)] pub authority: Signer<'info>, - #[account(mut, constraint = investor_fiat.owner == *authority.key)] - pub investor_fiat: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_deposit_token.owner == *authority.key)] + pub depositor_deposit_token: Account<'info, TokenAccount>, - #[account(mut, constraint = investor_redeemable.owner == *authority.key)] - pub investor_redeemable: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_redeemable.owner == *authority.key)] + pub depositor_redeemable: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, - - pub clock: Sysvar<'info, Clock>, } ``` -We need the redeemable and fiat token mint accounts. This is because we need to know which tokens we will be dealing with in this instruction. +We need the redeemable and deposit_token mint accounts. This is because we need to know which tokens we will be dealing with in this instruction. -We also need the fiat `TokenAccount`s owned by the pool_signer and the investor. This is because we need to know from which account to which account tokens need to be transferred. In this case, fiat tokens will be transferred from the investor’s fiat `TokenAccount` to pool_signer’s fiat `TokenAccount`. +We also need the deposit_token `TokenAccount`s owned by the pool_signer and the depositor. This is because we need to know from which account to which account tokens need to be transferred. In this case, deposit tokens will be transferred from the depositor’s `TokenAccount` to pool_signer’s `TokenAccount`. -And finally, we need to know the investor’s redeemable `TokenAccount` to be able to mint redeemable token accounts to the investor. +And finally, we need to know the depositor’s redeemable `TokenAccount` to be able to mint redeemable token accounts to the depositor. Let’s add the handler function. Add this after the `initialize_pool` function ```rust -#[access_control(unrestricted_phase(&ctx))] - pub fn exchange_fiat_for_redeemable( - ctx: Context, + // --snip-- + + #[access_control(unrestricted_phase(&ctx))] + pub fn exchange_deposit_token_for_redeemable( + ctx: Context, amount: u64, ) -> Result<()> { if amount == 0 { return Err(ErrorCode::InvalidParameter.into()); } // While token::transfer will check this, we prefer a verbose error msg - if ctx.accounts.investor_fiat.amount < amount { - return Err(ErrorCode::LowFiat.into()); + if ctx.accounts.depositor_deposit_token.amount < amount { + return Err(ErrorCode::LowDepositToken.into()); } - // Transfer investor's fiat to pool fiat account. + // Transfer depositor's deposit_token to pool deposit_token account. let cpi_accounts = Transfer { - from: ctx.accounts.investor_fiat.to_account_info(), - to: ctx.accounts.pool_fiat.to_account_info(), - authority: ctx.accounts.authority.to_account_info().clone(), + from: ctx.accounts.depositor_deposit_token.to_account_info(), + to: ctx.accounts.pool_deposit_token.to_account_info(), + authority: ctx.accounts.authority.to_account_info(), }; let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); token::transfer(cpi_ctx, amount)?; - // Mint Redeemable to investor Redeemable account. + // Mint Redeemable to depositor Redeemable account. let seeds = &[ - ctx.accounts.pool.native_mint.as_ref(), - &[ctx.accounts.pool.bump], + ctx.accounts.pool.native_mint.as_ref(), + &[ctx.accounts.pool.bump], ]; let signer = &[&seeds[..]]; let cpi_accounts = MintTo { mint: ctx.accounts.redeemable_mint.to_account_info(), - to: ctx.accounts.investor_redeemable.to_account_info(), + to: ctx.accounts.depositor_redeemable.to_account_info(), authority: ctx.accounts.pool_signer.clone(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); token::mint_to(cpi_ctx, amount)?; @@ -422,29 +396,29 @@ Let’s add the handler function. Add this after the `initialize_pool` function } ``` -The handler function takes two parameters, the context, and the amount of fiat tokens to be sent. +The handler function takes two parameters, the context, and the amount of deposit tokens to be sent. The first thing we do is check if the amount given as the parameter is 0 or not, and throw an error if it is. -Then we check if the investor’s fiat TokenAccount has a lesser amount of tokens than the amount specified. Although the `token::transfer` call already checks it, we want a verbose error message. +Then we check if the depositor’s deposit tokenAccount has a lesser amount of tokens than the amount specified. Although the `token::transfer` call already checks it, we want a verbose error message. -We create a new context with a `Transfer` object where we declare that tokens will be sent from the investor’s fiat TokenAccount to the pool’s fiat TokenAccount. +We create a new context with a `Transfer` object where we declare that tokens will be sent from the depositor’s deposit token account to the pool’s deposit token account. We create a new `CpiContext` with the token program and the accounts context. -We then invoke `token::transfer` to transfer `amount` number of fiat tokens +We then invoke `token::transfer` to transfer `amount` number of deposit tokens. -Now we need a mechanism to track which investor deposited how much to the pool, since we don’t store the data anywhere. The easiest way to do that is to send an equal number of tokens to the investor which they can use to redeem the native tokens once the IDO is over (Hence the name redeemable tokens). The authority to mint these tokens rests with the `pool_signer` which we check in `redeemable_mint`'s constraint in the accounts struct. These tokens don’t hold any value, and exactly the same number of redeemable tokens will be minted as the number of fiat tokens sent to the pool. +Now we need a mechanism to track which depositor deposited how much to the pool, since we don’t store the data anywhere. The easiest way to do that is to send an equal number of tokens to the depositor which they can use to redeem the native tokens once the IDO is over (Hence the name redeemable tokens). The authority to mint these tokens rests with the `pool_signer` which we check in `redeemable_mint`'s constraint in the accounts struct. These tokens don’t hold any value, and exactly the same number of redeemable tokens will be minted as the number of deposit tokens sent to the pool. Now, since a PDA needs to sign a transaction to mint redeemable tokens, we have to invoke `CpiContext::new_with_signer()` instead of `CpiContext::new()`. And to get the signer, we need to construct the PDA seeds. We construct the seeds with the `native_mint` address and bump we already store in the pool account to get the signer. -A `MintTo` context is created. Here we need to tell the program the mint account of the tokens that will be minted, the investor’s token account to where the redeemable tokens will be sent, and the authority of the transaction which is the `pool_signer`. +A `MintTo` context is created. Here we need to tell the program the mint account of the tokens that will be minted, the depositor’s token account to where the redeemable tokens will be sent, and the authority of the transaction which is the `pool_signer`. We invoke `CpiContext::new_with_signer()` with the token program, the accounts context and the signer which we constructed. -Finally, we call `token::mint_to` to send an equal number of redeemable tokens to the investor’s token account as the number of fiat tokens sent to the pool. +Finally, we call `token::mint_to` to send an equal number of redeemable tokens to the depositor’s token account as the number of deposit tokens sent to the pool. As a final step, add the errors and access control function, and build the program. @@ -455,33 +429,31 @@ pub enum ErrorCode { NonSequentialTimestamps, #[msg("Invalid Parameter")] InvalidParameter, - #[msg("Invalid Bump")] - InvalidBump, #[msg("IDO has not begun yet")] IdoFuture, #[msg("Not the correct time to invest")] WrongInvestingTime, - #[msg("Insufficient Fiat Tokens")] - LowFiat + #[msg("Insufficient deposit tokens")] + LowDepositToken } // Access Control Modifiers // IDO Starts in the Future -fn pre_ido_phase<'info>(ctx: &Context>, start_ido_ts: i64) -> Result<()> { - if !(ctx.accounts.clock.unix_timestamp < start_ido_ts) { +fn pre_ido_phase<'info>(start_ido_ts: i64) -> Result<()> { + if !(get_timestamp() < start_ido_ts) { return Err(ErrorCode::IdoFuture.into()); } Ok(()) } // Unrestricted Phase -fn unrestricted_phase<'info>(ctx: &Context>) -> Result<()> { - if !( - ctx.accounts.pool.start_ido_ts < ctx.accounts.clock.unix_timestamp - && - ctx.accounts.pool.end_ido_ts > ctx.accounts.clock.unix_timestamp - ) { +fn unrestricted_phase<'info>( + ctx: &Context>, +) -> Result<()> { + if !(ctx.accounts.pool.start_ido_ts < get_timestamp() + && ctx.accounts.pool.end_ido_ts > get_timestamp()) + { return Err(ErrorCode::WrongInvestingTime.into()); } Ok(()) @@ -492,7 +464,7 @@ The access control function `unrestricted_phase` ensures that deposits are limit Run `anchor build` -### Exchanging Investor’s Redeemable Tokens for Native Tokens +### Exchanging depositor’s Redeemable Tokens for Native Tokens The instruction and handler function for this will look very similar to just the previous one. Let’s look at `ExchangeRedeemableForNative`. @@ -505,11 +477,11 @@ pub struct ExchangeRedeemableForNative<'info> { /// CHECK: This is not dangerous #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] - pool_signer: AccountInfo<'info>, + pub pool_signer: AccountInfo<'info>, #[account( mut, - constraint = redeemable_mint.mint_authority == COption::Some(*pool_signer.key) + mint::authority = pool_signer )] pub redeemable_mint: Account<'info, Mint>, @@ -519,16 +491,13 @@ pub struct ExchangeRedeemableForNative<'info> { #[account(mut)] pub authority: Signer<'info>, - #[account(mut, constraint = investor_native.owner == *authority.key)] - pub investor_native: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_native.owner == *authority.key)] + pub depositor_native: Account<'info, TokenAccount>, - #[account(mut, constraint = investor_redeemable.owner == *authority.key)] - pub investor_redeemable: Account<'info, TokenAccount>, + #[account(mut, constraint = depositor_redeemable.owner == *authority.key)] + pub depositor_redeemable: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, - - pub clock: Sysvar<'info, Clock>, } ``` @@ -537,27 +506,26 @@ This is the same as the previous instruction, the only difference being we are d We can move on to the `exchange_redeemable_for_native` handler function. ```rust -#[access_control(ido_over(&ctx.accounts.pool, &ctx.accounts.clock))] - pub fn exchange_redeemable_for_native( - ctx: Context, - ) -> Result<()> { - let native_amount = (ctx.accounts.investor_redeemable.amount as u128) - .checked_mul(ctx.accounts.pool_native.amount as u128) - .unwrap() - .checked_div(ctx.accounts.redeemable_mint.supply as u128) - .unwrap(); + // --snip-- + + #[access_control(ido_over(&ctx.accounts.pool))] + pub fn exchange_redeemable_for_native(ctx: Context) -> Result<()> { + let native_amount = (ctx.accounts.depositor_redeemable.amount as u128) + .checked_mul(ctx.accounts.pool_native.amount as u128) + .unwrap() + .checked_div(ctx.accounts.redeemable_mint.supply as u128) + .unwrap(); let cpi_accounts = Burn { mint: ctx.accounts.redeemable_mint.to_account_info(), - from: ctx.accounts.investor_redeemable.to_account_info(), + from: ctx.accounts.depositor_redeemable.to_account_info(), authority: ctx.accounts.authority.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - token::burn(cpi_ctx, ctx.accounts.investor_redeemable.amount)?; - + token::burn(cpi_ctx, ctx.accounts.depositor_redeemable.amount)?; let seeds = &[ ctx.accounts.pool.native_mint.as_ref(), @@ -567,11 +535,11 @@ We can move on to the `exchange_redeemable_for_native` handler function. let cpi_accounts = Transfer { from: ctx.accounts.pool_native.to_account_info(), - to: ctx.accounts.investor_native.to_account_info(), + to: ctx.accounts.depositor_native.to_account_info(), authority: ctx.accounts.pool_signer.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); token::transfer(cpi_ctx, native_amount as u64)?; @@ -582,25 +550,22 @@ We can move on to the `exchange_redeemable_for_native` handler function. Here we go through a few new steps. -The first step is to calculate how much native tokens the investor will be receiving. The simple formula is +The first step is to calculate how much native tokens the depositor will be receiving. The simple formula is -(Amount of Redeemable Tokens with Investor \* Amount of Native Tokens in Pool) / Supply of Redeemable Tokens +(Amount of Redeemable Tokens with depositor \* Amount of Native Tokens in Pool) / Supply of Redeemable Tokens -The next step is to `Burn` the investor’s redeemable tokens. We create a `Burn` instance where we declare the redeemable token mint, investor’s token account from where the tokens will be burned and the authority. +The next step is to `Burn` the depositor’s redeemable tokens. We create a `Burn` instance where we declare the redeemable token mint, depositor’s token account from where the tokens will be burned and the authority. We create a new `CpiContext` with the token program and invoke `token::burn()`. -We then move on to calculate the seeds of the pool_signer and create a `Transfer` instance. Finally, we transfer the calculated amount of native tokens from `pool_native` to `investor_native`. +We then move on to calculate the seeds of the pool_signer and create a `Transfer` instance. Finally, we transfer the calculated amount of native tokens from `pool_native` to `depositor_native`. We are not done yet, the final thing left to do is write the access control function `ido_over()` ```rust //iDO Over -fn ido_over<'info>( - pool_account: &Account<'info, PoolAccount>, - clock: &Sysvar<'info, Clock>, -) -> Result<()> { - if !(pool_account.end_ido_ts < clock.unix_timestamp) { +fn ido_over<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { + if !(pool_account.end_ido_ts < get_timestamp()) { return Err(ErrorCode::IdoNotOver.into()); } Ok(()) @@ -618,14 +583,12 @@ pub enum ErrorCode { NonSequentialTimestamps, #[msg("Invalid Parameter")] InvalidParameter, - #[msg("Invalid Bump")] - InvalidBump, #[msg("IDO has not begun yet")] IdoFuture, #[msg("Not the correct time to invest")] WrongInvestingTime, - #[msg("Insufficient Fiat Tokens")] - LowFiat, + #[msg("Insufficient deposit tokens")] + LowDepositToken, #[msg("IDO has not ended yet")] IdoNotOver } @@ -633,75 +596,72 @@ pub enum ErrorCode { Run `anchor build`. You will see now that the warnings have disappeared. -### Withdrawing Fiat Tokens From the Pool +### Withdrawing Deposit Tokens From the Pool Now the final piece of the program that’s left is allowing the project to withdraw funds from the pool. Add this in the accounts struct ```rust #[derive(Accounts)] -pub struct WithdrawPoolFiat<'info> { - #[account(has_one = pool_fiat)] +pub struct WithdrawPoolDepositToken<'info> { + #[account(has_one = pool_deposit_token)] pub pool: Box>, ///CHECK: This is not dangerous #[account(seeds = [pool.native_mint.as_ref()], bump = pool.bump)] pub pool_signer: AccountInfo<'info>, - #[account(mut, constraint = pool_fiat.mint == *fiat_mint.to_account_info().key)] - pub fiat_mint: Account<'info, Mint>, + #[account(address = pool_deposit_token.mint)] + pub deposit_token_mint: Account<'info, Mint>, - #[account(mut, constraint = pool_fiat.owner == *pool_signer.key)] - pub pool_fiat: Account<'info, TokenAccount>, + #[account(mut, constraint = pool_deposit_token.owner == *pool_signer.key)] + pub pool_deposit_token: Account<'info, TokenAccount>, #[account(mut)] pub payer: Signer<'info>, #[account(mut)] - pub creator_fiat: Account<'info, TokenAccount>, + pub creator_deposit_token: Account<'info, TokenAccount>, - #[account(constraint = token_program.key == &token::ID)] pub token_program: Program<'info, Token>, - - pub clock: Sysvar<'info, Clock>, } ``` -Here we need the project authority to sign the transaction to withdraw the pool fiat tokens. +Here we need the project authority to sign the transaction to withdraw the pool deposit tokens. -Add in the handler function `withdraw_pool_fiat` +Add in the handler function `withdraw_pool_deposit_token` ```rust -#[access_control(can_withdraw_fiat(&ctx.accounts.pool, &ctx.accounts.clock))] - pub fn withdraw_pool_fiat(ctx: Context) -> Result<()> { + // --snip-- + + #[access_control(can_withdraw_deposit_token(&ctx.accounts.pool))] + pub fn withdraw_pool_deposit_token(ctx: Context) -> Result<()> { let seeds = &[ ctx.accounts.pool.native_mint.as_ref(), &[ctx.accounts.pool.bump], ]; let signer = &[&seeds[..]]; let cpi_accounts = Transfer { - from: ctx.accounts.pool_fiat.to_account_info(), - to: ctx.accounts.creator_fiat.to_account_info(), + from: ctx.accounts.pool_deposit_token.to_account_info(), + to: ctx.accounts.creator_deposit_token.to_account_info(), authority: ctx.accounts.pool_signer.to_account_info(), }; - let cpi_program = ctx.accounts.token_program.to_account_info().clone(); + let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, ctx.accounts.pool_fiat.amount)?; + token::transfer(cpi_ctx, ctx.accounts.pool_deposit_token.amount)?; Ok(()) } +} ``` -Again, here we do a similar thing. Calculate the signer seeds of pool_signer, Create a new `CpiContext` with the signer and invoke `token::transfer()` to transfer all fiat tokens from the pool to the project’s fiat `TokenAccount` +Again, here we do a similar thing. Calculate the signer seeds of pool_signer, Create a new `CpiContext` with the signer and invoke `token::transfer()` to transfer all deposit tokens from the pool to the project’s deposit `TokenAccount` Add the access control function ```rust -//Can Withdraw fiat -fn can_withdraw_fiat<'info>( - pool_account: &Account<'info, PoolAccount>, - clock: &Sysvar<'info, Clock>, -) -> Result<()> { - if !(pool_account.withdraw_fiat_ts < clock.unix_timestamp) { +//Can Withdraw deposit_token +fn can_withdraw_deposit_token<'info>(pool_account: &Account<'info, PoolAccount>) -> Result<()> { + if !(pool_account.withdraw_deposit_token_ts < get_timestamp()) { return Err(ErrorCode::CannotWithdrawYet.into()); } Ok(()) @@ -717,17 +677,15 @@ pub enum ErrorCode { NonSequentialTimestamps, #[msg("Invalid Parameter")] InvalidParameter, - #[msg("Invalid Bump")] - InvalidBump, #[msg("IDO has not begun yet")] IdoFuture, #[msg("Not the correct time to invest")] WrongInvestingTime, - #[msg("Insufficient Fiat Tokens")] - LowFiat, + #[msg("Insufficient deposit tokens")] + Lowdeposit token, #[msg("IDO has not ended yet")] IdoNotOver, - #[msg("Cannot withdraw Fiat yet")] + #[msg("Cannot withdraw deposit token yet")] CannotWithdrawYet } ``` @@ -803,32 +761,32 @@ describe("ido-program", () => { let nativeTokenAmount = new anchor.BN(1000000); - let fiatMint: PublicKey; + let depositTokenMint: PublicKey; let redeemableMint: PublicKey; let nativeMint: PublicKey; - let projectFiat: PublicKey; - let projectNative: PublicKey; + let projectDepositAccount: PublicKey; + let projectNativeAccount: PublicKey; - let investorFiat: PublicKey; - let investorNative: PublicKey; - let investorRedeemable: PublicKey; + let depositorDepositAccount: PublicKey; + let depositorNativeAccount: PublicKey; + let depositorRedeemable: PublicKey; let poolNative: PublicKey; - let poolFiat: PublicKey; + let poolDepositToken: PublicKey; let poolSigner: PublicKey; let nowBn: anchor.BN; let startIdoTs: anchor.BN; let endIdoTs: anchor.BN; - let withDrawFiatTs: anchor.BN; + let withDrawDepositTokenTs: anchor.BN; const payer = anchor.web3.Keypair.generate(); const mintAuthority = anchor.web3.Keypair.generate(); const project = anchor.web3.Keypair.generate(); - const investor = anchor.web3.Keypair.generate(); + const depositor = anchor.web3.Keypair.generate(); let pool = anchor.web3.Keypair.generate(); }); @@ -841,7 +799,7 @@ Now let’s write the first test ```ts //--snip-- - it("Can initialize the program state", async () => { + it("Can initialize the program state", async () => { const transferSig = await provider.connection.requestAirdrop( payer.publicKey, 10000000000 @@ -870,80 +828,62 @@ Now let’s write the first test }), SystemProgram.transfer({ fromPubkey: payer.publicKey, - toPubkey: investor.publicKey, + toPubkey: depositor.publicKey, lamports: 2000000000, }) ); await provider.sendAndConfirm(tx, [payer]); - fiatMint = await createMint( + depositTokenMint = await createMint( provider.connection, payer, mintAuthority.publicKey, - undefined, - 0, - undefined, - undefined, - TOKEN_PROGRAM_ID + null, + 0 ); nativeMint = await createMint( provider.connection, payer, mintAuthority.publicKey, - undefined, - 0, - undefined, - undefined, - TOKEN_PROGRAM_ID + null, + 0 ); - projectFiat = await createAccount( + projectDepositAccount = await createAccount( provider.connection, payer, - fiatMint, - project.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + depositTokenMint, + project.publicKey ); - projectNative = await createAccount( + projectNativeAccount = await createAccount( provider.connection, payer, nativeMint, - project.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + project.publicKey ); - investorFiat = await createAccount( + depositorDepositAccount = await createAccount( provider.connection, payer, - fiatMint, - investor.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + depositTokenMint, + depositor.publicKey ); - investorNative = await createAccount( + depositorNativeAccount = await createAccount( provider.connection, payer, nativeMint, - investor.publicKey, - undefined, - undefined, - TOKEN_PROGRAM_ID + depositor.publicKey ); await mintTo( provider.connection, payer, nativeMint, - projectNative, + projectNativeAccount, mintAuthority, nativeTokenAmount.toNumber() ); @@ -951,32 +891,32 @@ Now let’s write the first test await mintTo( provider.connection, payer, - fiatMint, - investorFiat, + depositTokenMint, + depositorDepositAccount, mintAuthority, 10000 ); - const projectNativeTokenAccount = await getAccount( + const projectNativeAccountTokenAccount = await getAccount( provider.connection, - projectNative + projectNativeAccount ); assert.strictEqual( - projectNativeTokenAccount.amount.toString(), + projectNativeAccountTokenAccount.amount.toString(), nativeTokenAmount.toNumber().toString() ); }); }) ``` -The first thing we did was request 10 SOL to the `payer` account. Then we transfer 2 SOL each to `mintAuthority`, `project` and `investor` accounts so that they can pay rent and gas fees. +The first thing we did was request 10 SOL to the `payer` account. Then we transfer 2 SOL each to `mintAuthority`, `project` and `depositor` accounts so that they can pay rent and gas fees. -Then we create dummy `fiatMint` and `nativeMint` accounts for testing purposes. +Then we create dummy `deposit tokenMint` and `nativeMint` accounts for testing purposes. -We create `TokenAccount`s for these mints owned by the `project` and the `investor`. +We create `TokenAccount`s for these mints owned by the `project` and the `depositor`. -Finally we mint native tokens to the `project` and fiat tokens to the `investor`. +Finally we mint native tokens to the `project` and deposit tokens to the `depositor`. Run `anchor test`. It should show 1 test passed. @@ -1007,11 +947,11 @@ Now we are ready to test out our instructions. TOKEN_PROGRAM_ID ); - investorRedeemable = await createAccount( + depositorRedeemable = await createAccount( provider.connection, payer, redeemableMint, - investor.publicKey, + depositor.publicKey, undefined, undefined, TOKEN_PROGRAM_ID @@ -1031,10 +971,10 @@ Now we are ready to test out our instructions. poolNative = poolNativeAccount.address; - let poolFiatAccount = await getOrCreateAssociatedTokenAccount( + let pooldeposit tokenAccount = await getOrCreateAssociatedTokenAccount( provider.connection, payer, - fiatMint, + deposit tokenMint, poolSigner, true, undefined, @@ -1043,29 +983,29 @@ Now we are ready to test out our instructions. undefined ); - poolFiat = poolFiatAccount.address; + pooldeposit token = pooldeposit tokenAccount.address; nowBn = new anchor.BN(Date.now() / 1000); startIdoTs = nowBn.add(new anchor.BN(10)); endIdoTs = nowBn.add(new anchor.BN(20)); - withDrawFiatTs = nowBn.add(new anchor.BN(30)); + withDrawdeposit tokenTs = nowBn.add(new anchor.BN(30)); await program.methods .initializePool( nativeTokenAmount, startIdoTs, endIdoTs, - withDrawFiatTs, + withDrawdeposit tokenTs, bump ) .accounts({ pool: pool.publicKey, poolSigner: poolSigner, redeemableMint: redeemableMint, - fiatMint: fiatMint, + deposit tokenMint: deposit tokenMint, nativeMint: nativeMint, poolNative: poolNative, - poolFiat: poolFiat, + pooldeposit token: pooldeposit token, authority: project.publicKey, creatorNative: projectNative, tokenProgram: TOKEN_PROGRAM_ID, @@ -1107,8 +1047,8 @@ Now we are ready to test out our instructions. nativeMint.toBase58() ); assert.strictEqual( - createdPool.poolFiat.toBase58(), - poolFiat.toBase58() + createdPool.pooldeposit token.toBase58(), + pooldeposit token.toBase58() ); assert.strictEqual( createdPool.totalNativeTokens.toNumber().toString(), @@ -1123,80 +1063,79 @@ Now we are ready to test out our instructions. endIdoTs.toString() ); assert.strictEqual( - createdPool.withdrawFiatTs.toNumber().toString(), - withDrawFiatTs.toString() + createdPool.withdrawdeposit tokenTs.toNumber().toString(), + withDrawdeposit tokenTs.toString() ); }); }) ``` -First we derive the seeds using nativeMint to get the poolSigner PDA. Then we create the redeemable token mint account and the necessary token accounts owned by poolSIgner by calling `getOrCreateAssociatedTokenAccount()`. Then we set the different timestamps which we need in the program and call the `program.methods.initializePool()` method. We pass in all the accounts we defined in the accounts struct of our program and the signers, which are `pool` and `project`. +First we derive the seeds using nativeMint to get the poolSigner PDA. Then we create the redeemable token mint account and the necessary token accounts owned by poolSigner by calling `getOrCreateAssociatedTokenAccount()`. Then we set the different timestamps which we need in the program and call the `program.methods.initializePool()` method. We pass in all the accounts we defined in the accounts struct of our program and the signers, which are `pool` and `project`. Run `anchor test`. It should show 2 tests passed. -### Exchange Investor Fiat for Redeemable Tokens +### Exchange depositor deposit token for Redeemable Tokens ```ts //--snip-- let deposit = 5000; - it("Can exchange investor Fiat for redeemable tokens", async () => { + it("Can exchange depositor Deposit tokens for Redeemable tokens", async () => { if (Date.now() < startIdoTs.toNumber() * 1000) { await sleep(startIdoTs.toNumber() * 1000 - Date.now() + 5000); } await program.methods - .exchangeFiatForRedeemable(new anchor.BN(deposit)) + .exchangeDepositTokenForRedeemable(new anchor.BN(firstDeposit)) .accounts({ pool: pool.publicKey, poolSigner: poolSigner, redeemableMint: redeemableMint, - fiatMint: fiatMint, - poolFiat: poolFiat, - authority: investor.publicKey, - investorFiat: investorFiat, - investorRedeemable: investorRedeemable, + depositTokenMint: depositTokenMint, + poolDepositToken: poolDepositToken, + authority: depositor.publicKey, + depositorDepositToken: depositorDepositAccount, + depositorRedeemable: depositorRedeemable, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }) - .signers([investor]) + .signers([depositor]) .rpc(); - const poolFiatTokenAccount = await getAccount( + const poolDepositTokenTokenAccount = await getAccount( provider.connection, - poolFiat + poolDepositToken ); assert.strictEqual( - poolFiatTokenAccount.amount.toString(), - deposit.toString() + poolDepositTokenTokenAccount.amount.toString(), + firstDeposit.toString() ); - const investorFiatTokenAccount = await getAccount( + const depositorDepositAccountTokenAccount = await getAccount( provider.connection, - investorFiat + depositorDepositAccount ); - const investorRedeemableTokenAccount = await getAccount( + const depositorRedeemableTokenAccount = await getAccount( provider.connection, - investorRedeemable + depositorRedeemable ); assert.strictEqual( - investorFiatTokenAccount.amount.toString(), - (10000 - deposit).toString() + depositorDepositAccountTokenAccount.amount.toString(), + (10000 - firstDeposit).toString() ); assert.strictEqual( - investorRedeemableTokenAccount.amount.toString(), - deposit.toString() + depositorRedeemableTokenAccount.amount.toString(), + firstDeposit.toString() ); }); }) ``` -We check for the current time and `sleep` before it is time to call the `exchangeFiatForRedeemable` method. We cannot call the method outside the stipulated time because of the access controls we added. If we try to call it outside the correct time, it will error out. +We check for the current time and `sleep` before it is time to call the `exchangeDepositTokenForRedeemable` method. We cannot call the method outside the stipulated time because of the access controls we added. If we try to call it outside the correct time, it will error out. But we have not added the `sleep` function yet. Add it at the end outside the `describe` block. @@ -1207,7 +1146,7 @@ function sleep(ms: number) { } ``` -Inside the test, we call the `exchangeFiatForRedeemable` program method, and then write the usual assert checks. +Inside the test, we call the `exchangeDepositTokenForRedeemable` program method, and then write the usual assert checks. Running `anchor test` should now show 3 tests passed. @@ -1215,7 +1154,7 @@ Running `anchor test` should now show 3 tests passed. ```ts //--snip-- - it("Can exchange investor Redeemable tokens for Native tokens", async () => { + it("Can exchange depositor Redeemable tokens for Native tokens", async () => { if (Date.now() < endIdoTs.toNumber() * 1000) { await sleep(endIdoTs.toNumber() * 1000 - Date.now() + 5000); } @@ -1227,13 +1166,12 @@ Running `anchor test` should now show 3 tests passed. poolSigner: poolSigner, redeemableMint: redeemableMint, poolNative: poolNative, - authority: investor.publicKey, - investorNative: investorNative, - investorRedeemable: investorRedeemable, + authority: depositor.publicKey, + depositorNative: depositorNativeAccount, + depositorRedeemable: depositorRedeemable, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }) - .signers([investor]) + .signers([depositor]) .rpc(); const poolNativeTokenAccount = await getAccount( @@ -1243,23 +1181,23 @@ Running `anchor test` should now show 3 tests passed. assert.strictEqual(poolNativeTokenAccount.amount.toString(), "0"); - const investorNativeTokenAccount = await getAccount( + const depositorNativeAccountTokenAccount = await getAccount( provider.connection, - investorNative + depositorNativeAccount ); - const investorRedeemableTokenAccount = await getAccount( + const depositorRedeemableTokenAccount = await getAccount( provider.connection, - investorRedeemable + depositorRedeemable ); assert.strictEqual( - investorNativeTokenAccount.amount.toString(), + depositorNativeAccountTokenAccount.amount.toString(), nativeTokenAmount.toString() ); assert.strictEqual( - investorRedeemableTokenAccount.amount.toString(), + depositorRedeemableTokenAccount.amount.toString(), "0" ); }); @@ -1270,50 +1208,49 @@ Here also, we go through similar steps like the previous one. `anchor test` should now show 4 tests passed. -### Withdraw Pool Fiat Tokens +### Withdraw Pool deposit tokens ```ts - it("Can withdraw total Fiat from pool account", async () => { - if (Date.now() < withDrawFiatTs.toNumber() * 1000) { - await sleep(withDrawFiatTs.toNumber() * 1000 - Date.now() + 5000); + it("Can withdraw total deposit tokens from pool account", async () => { + if (Date.now() < withDrawDepositTokenTs.toNumber() * 1000) { + await sleep(withDrawDepositTokenTs.toNumber() * 1000 - Date.now() + 5000); } await program.methods - .withdrawPoolFiat() + .withdrawPoolDepositToken() .accounts({ pool: pool.publicKey, poolSigner: poolSigner, - fiatMint: fiatMint, - poolFiat: poolFiat, + depositTokenMint: depositTokenMint, + poolDepositToken: poolDepositToken, payer: project.publicKey, - creatorFiat: projectFiat, + creatorDepositToken: projectDepositAccount, tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, }) .signers([project]) .rpc(); - const poolFiatTokenAccount = await getAccount( + const poolDepositTokenTokenAccount = await getAccount( provider.connection, - poolFiat + poolDepositToken ); - assert.strictEqual(poolFiatTokenAccount.amount.toString(), "0"); + assert.strictEqual(poolDepositTokenTokenAccount.amount.toString(), "0"); - const projectFiatTokenAccount = await getAccount( + const projectDepositAccountTokenAccount = await getAccount( provider.connection, - projectFiat + projectDepositAccount ); assert.strictEqual( - projectFiatTokenAccount.amount.toString(), - deposit.toString() + projectDepositAccountTokenAccount.amount.toString(), + firstDeposit.toString() ); }); -}); +}) ``` -Finally we test the `withdrawPoolFiat` program method, and that concludes all our tests. +Finally we test the `withdrawPoolDepositToken` program method, and that concludes all our tests. `anchor test` should now be showing 5 tests passed.