{"id":13524914,"url":"https://github.com/flowerpowerdao/power-equalizer","last_synced_at":"2025-04-01T04:30:25.159Z","repository":{"id":136699539,"uuid":"596975506","full_name":"flowerpowerdao/power-equalizer","owner":"flowerpowerdao","description":null,"archived":false,"fork":false,"pushed_at":"2023-09-01T08:56:12.000Z","size":1513,"stargazers_count":7,"open_issues_count":26,"forks_count":12,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-02T09:32:08.489Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Motoko","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flowerpowerdao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-03T10:50:03.000Z","updated_at":"2024-10-27T13:39:36.000Z","dependencies_parsed_at":"2024-07-27T03:04:10.236Z","dependency_job_id":null,"html_url":"https://github.com/flowerpowerdao/power-equalizer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flowerpowerdao%2Fpower-equalizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flowerpowerdao%2Fpower-equalizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flowerpowerdao%2Fpower-equalizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flowerpowerdao%2Fpower-equalizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flowerpowerdao","download_url":"https://codeload.github.com/flowerpowerdao/power-equalizer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246585506,"owners_count":20801022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T06:01:14.538Z","updated_at":"2025-04-01T04:30:24.770Z","avatar_url":"https://github.com/flowerpowerdao.png","language":"Motoko","funding_links":[],"categories":["Fungible and Non-fungible Tokens (NFTs)"],"sub_categories":["Implementations"],"readme":"![BP_FPDAO_Logo_BlackOnWhite_sRGB_2](https://user-images.githubusercontent.com/32162112/221128811-5484d430-c357-49e8-8b25-0f04695a5691.svg)\n\n# power equalizer 🌼\n\n## Development\n```\nnpm start\n```\n\nRedeploy when you changed Motoko code\n```\nnpm run deploy-local\n```\n\n## Steps to launch an NFT collection\n\n- [ ] fork this repository\n- [ ] comment out last 4 lines in `.gitignore`\n- [ ] prepare and deploy [assets](#assets)\n- [ ] copy `initArgs.local.did` to `initArgs.did` and adapt it to your needs (see [INIT_ARGS.md](INIT_ARGS.md))\n- [ ] [create canisters](#create-canisters)\n- [ ] [deploy NFT canister](#deploy-nft-canister)\n- [ ] create collection [summary](https://docs.google.com/document/d/1yady6xdsuInRbj8zLpIn7oG933UZUzg-vx_MUnr_jJ0/edit?usp=sharing)\n- [ ] add canister to [DAB](https://docs.google.com/forms/d/e/1FAIpQLSc-0BL9FMRtI0HhWj4g7CCYjf3TMr4_K_qqmagjzkUH_CKczw/viewform)\n- [ ] send collection details to entrepot via [form](https://collection-guide.paperform.co/)\n- [ ] top canister up with cycles\n- [ ] run off chain backup script with mainnet canister id\n- [ ] setup auto topup of canisters\n\nIf you want to upgrade an existing nft canister, see [upgrade nft canister](#upgrade-nft-canister)\n\n## Create canisters\nIf you want to create canisters w/o using cycles wallet:\n```\ndfx ledger create-canister --amount 0.1 --network ic $(dfx identity get-principal)\n```\n\n## Assets\n\nPlaceholder is optional.\n\nExtension can be any.\n\n`assets` folder structure:\n```\nassets/\n  metadata.json\n  placeholder.svg\n  1.svg\n  1_thumbnail.svg\n  2.svg\n  2_thumbnail.svg\n  ...\n```\n\nDeploy assets canister\n```\ndfx deploy assets --no-wallet --network ic\n```\n\n## Deploy NFT canister\nFor local deployment run extra commands:\n```\nnpm run replica\nnpm run deploy:ledger\n```\n\nDeploy\n```\nnpm run deploy \u003cnetwork\u003e\n```\n\n`\u003cnetwork\u003e` - local | test | staging | production\n\n`local` and `test` deployed locally\n\n`staging` and `production` deployed to IC mainnet\n\n### Examples\n\nClean deploy locally\n```\nnpm run deploy local -- --mode reinstall\n```\n\nDeploy staging\n```\nnpm run deploy staging\n```\n\nDeploy production\n```\nnpm run deploy production\n```\n\n## Upgrade nft canister\nUpgrade production canister\n```\nnpm run upgrade-production\n```\n\n## caveats 🕳\n\n- The canister code is written in a way that the seed animation _ALWAYS_ has to be the first asset uploaded to the canister if you are doing a `revealDelay \u003e 0`\n- The seed animation video needs to be encoded in a way that it can be played on iOS devices, use `HandBrake` for that or `ffmpeg`\n\n## vessel 🚢\n\n- Run `vessel verify --version 0.8.1` to verify everything still builds correctly after adding a new depdenceny\n- To use `vessels`s moc version when deploying, use `DFX_MOC_PATH=\"$(vessel bin)/moc\" dfx deploy`\n\n## shuffle 🔀\n\n- The shuffle uses the random beacon to derive a random seed for the PRNG\n- It basically shuffles all the assets in the `assets` stable variable\n- The linking inside the canister is\n\n```\ntokenIndex -\u003e assetIndex\nasset[assetIndex] -\u003e NFT\n```\n\n- by shuffling the assets, we are actually changing the mapping from `tokenIndex` to `NFT`\n- initially the `tokenIndex` matches the `assetIndex` (`assetIndex` = `tokenIndex+1`) and the `assetIndex` matches the `NFT` (`NFT` = `assetIndex+1`)\n- but after the shuffle the `assetIndex` and the `NFT` mint number no longer match\n- so so token at `tokenIndex` still points to the same asset at `assetIndex`, but this asset no longer has the same `NFT` mint number\n- we can always retrieve the `NFT` mint number from the `_asset[index].name` property which we specify when adding an asset to the canister\n\n## off-chain backup ⛓\n\nWe use the `getRegistry` (`tokenIndex -\u003e AccountIdentifier`) and `getTokenToAssetMapping` (`tokenIndex -\u003e NFT`) canister methods to backup state offchain. Therefore we simply use a script that queries the afore mentioned methods every 60 minutes and saves the responses on a server. You can find the script in `state_backup`. We are also submitting every transaction to `CAP`, which again offers off-chain backups of their data.\n\nNote that the indices of the json outputs represent the indices of the internal storage. E.g. index `0` means it is the first item in the array. In the UI (entrepot or stoic wallet) those indices are incremented by one, so they start with `1` and not with `0`.\n\nTo have the same token identifiers for the same tokens, it is important to keep the order of the minting when reinstantiating the canister.\n\nSo when executing `mintNFT`, the `to` address is taken from `registry.json` and the `asset` is taken from `tokens.json`. It's important here that the uploading of the assets is on order (start with flower 1, end with flower 2009) and that the `assets` index 0 is used by something other than an NFT asset (before it was the seed animation)!\n\n## Testing 🧪\n\nEach test suite is deployed with its own env settings.\n\nFirst, start a local replica\n\n```\nnpm run replica\n```\n\nDeploy and run all unit and e2e tests\n\n```\nnpm run test\n```\n\nRun only unit tests\n\n```\nnpm run test:unit\n```\n\nDeploy and run specific e2e tests\n\n```\nnpm run test:e2e pending-sale\n```\n\nRun tests without deployment (useful when writing tests)\n\n```\nnpm run vitest\n```\n\nor\n\n```\nnpm run vitest:watch\n```\n\nor run specific test suite\n\n```\nnpm run vitest pending-sale\n```\n\n## Manual testing 🧪\n\nDeploy [assets](#assets) and [NFT canister](#deploy-nft-canister).\n\nMake sure that the NFT is redirected to the asset:\n\nhttp://localhost:4943/0?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\n\nhttp://localhost:4943/1?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\n\nhttp://localhost:4943/1?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\u0026type=thumbnail\n\nhttp://localhost:4943/?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\u0026type=thumbnail\u0026asset=1\n\nhttp://localhost:4943/?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\u0026tokenid=rrkah-fqaaa-aaaaa-aaaaq-cai should have same redirect as http://localhost:4943/0?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\n\n\n**NOTE**\n\nyou can also use `http://127.0.0.1:8000/?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\u0026asset=0` or `http://127.0.0.1:8000/1.svg?canisterId=rrkah-fqaaa-aaaaa-aaaaq-cai\u0026asset=0` locally\n\n---\n\nadd the following line to `/etc/hosts` if on mac\n\n```\n127.0.0.1       rrkah-fqaaa-aaaaa-aaaaq-cai.localhost\n```\n\nthe canister can now be accesed with\n\n```\nhttp://rwlgt-iiaaa-aaaaa-aaaaa-cai.localhost:8453/?tokenid=rwlgt-iiaaa-aaaaa-aaaaa-cai\n```\n\nor via command line with\n\n```\ncurl \"rwlgt-iiaaa-aaaaa-aaaaa-cai.localhost:8453/?tokenid=rwlgt-iiaaa-aaaaa-aaaaa-cai\"\n```\n\n\u003ch2 id=\"ext\"\u003eext-cli 🔌\u003c/h2\u003e\n\nto get the tokenid from the canister and index do the following\n\n1. clone https://github.com/Toniq-Labs/ext-cli and https://github.com/Toniq-Labs/ext-js in the same directory\n2. run `npm i -g` from within `ext-cli`\n3. run `ext token \u003ccanister_id\u003e \u003cindex\u003e`\n\n## settlements\n\n- if there's a settlement that didn't work, we can call the `settlements` query method and then `settle` using the index to settle the transaction\n\n- if there a salesSettelemnts that didnt work, we call the `salesSettlements` query method and then `retrieve` using the address to settle the transaction\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflowerpowerdao%2Fpower-equalizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflowerpowerdao%2Fpower-equalizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflowerpowerdao%2Fpower-equalizer/lists"}