From 67eb2509f819035113a243fa030be578d855e3fa Mon Sep 17 00:00:00 2001 From: peaceandwhisky Date: Fri, 1 Nov 2024 12:31:03 +0900 Subject: [PATCH 1/2] update getting-started --- docs/develop/astraeus/get-started.md | 125 ------- docs/develop/astraeus/getting-started.md | 413 +++++++++++++++++++++++ 2 files changed, 413 insertions(+), 125 deletions(-) delete mode 100644 docs/develop/astraeus/get-started.md create mode 100644 docs/develop/astraeus/getting-started.md diff --git a/docs/develop/astraeus/get-started.md b/docs/develop/astraeus/get-started.md deleted file mode 100644 index db6fe58..0000000 --- a/docs/develop/astraeus/get-started.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Get Started - -This guide will help you set up the Astraeus API server on the Suave Toliman Testnet to easily use Transferable Accounts (TA). You will learn how to create, approve, and transfer TAs using the API server. Additionally, you will learn how to sign transactions on external chains using TAs. - -### Prerequisites - -- An environment capable of running Docker -- Two accounts on the Toliman Testnet with access to their private keys and TEETH tokens - -If you do not have TEETH tokens, you can obtain them from the [Toliman Testnet Faucet](https://faucet.toliman.suave.flashbots.net/). - -### Steps - -1. **Copy the example environment file to create your own `.env` file:** - - ``` - cp .env.example .env - ``` - -2. **Edit the `.env` file and set the `PRIVATE_KEY` to the private key of an account that holds tokens on the Suave network.** - -3. **Start the API server using Docker:** - - ``` - make run-api-server-docker - ``` - - At this point, the API server should be running locally via Docker. - -4. **Generate the Timed Signature required for API requests. Replace `validFor` with the UnixTime until which the signature is valid, and `your_private_key` with the private key of your account:** - - Generate signatures for both of your accounts. - - ``` - go run scripts/utils/generate_timed_signature/main.go 1759244400(validFor) 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94(your_private_key) - Address: 1b1374742cb5f84b1ef167db57236350380084e1 - Message Hash: 2f9cc010a830e69220428772a000f8c057229d5c8e668ec86d5d74cf07cffcd2 - Signature: 31f8e01e19bfb2bf66f01e34006eef1ac9a3384a71832779e768b80c7a5f1b3c6db26a20a99abce0d612046e4e0b6a73c1da892f0f98a41155a9a4998f29dcbd1c - ``` - -5. **Create Account Request to API Server** - - Execute the request to create a TA. Use the output from step 4 in the `proof` section: - - ``` - curl -X POST http://localhost:8080/v1/accounts -d '{ - "proof": { - "validFor": 1759244400, - "messageHash": "2f9cc010a830e69220428772a000f8c057229d5c8e668ec86d5d74cf07cffcd2", - "signature": "31f8e01e19bfb2bf66f01e34006eef1ac9a3384a71832779e768b80c7a5f1b3c6db26a20a99abce0d612046e4e0b6a73c1da892f0f98a41155a9a4998f29dcbd1c", - "signer": "1b1374742cb5f84b1ef167db57236350380084e1" - } - }' - {"txHash":"0x87deac0a0982fcc41bc915f01c4924f27c1b1b16882d04e64d0f3e1442314fea", "accountId":"0x65c987ba099153e19942c809e2289120"} - ``` - - Once the `txHash` is displayed, the account creation is complete. The displayed `accountId` is the ID of the account you created. - -6. **Approve Address Request to API Server** - - Approve the transfer of TA ownership from the current account to another account. - - Specify the `account_id` of the created account. Use the same values as before, and input the address of the other account in the `address` field. - - ``` - curl -s -X POST http://localhost:8080/v1/accounts/$create_account_account_id/approve -d '{ - "base": { - "account_id": "0xb06e9fd4baf654208e7886284cdcdab2", - "proof": { - "validFor": "1726946480", - "messageHash": "32948247c695a2545f9b35c040a293f1c6cd300062e9d7abdf0b3ed2a7b596d1", - "signature": "50346a31ad859f211294496e01083dcb85803bb27923b8d256756c71bdbfe36e1e89741215f58fd4bb8db42f04775303e60748b99f10c64e189d1f585d6b77531c", - "signer": "1b1374742cb5f84b1ef167db57236350380084e1" - } - }, - "address": "your_another_account_address" - }' - ``` - - Once the `txHash` is displayed, the account approval is complete. - -7. **Transfer Account Request to API Server** - - Execute the transfer of TA ownership. This can be done by either the current TA owner or the approved account. - In this example, the transfer is executed with the signature of the TA creator, but you can also create and execute the request with the signature of the recipient. - - ``` - curl -s -X POST http://localhost:8080/v1/accounts/$create_account_account_id/transfer -d '{ - "base": { - "account_id": "0xb06e9fd4baf654208e7886284cdcdab2", - "proof": { - "validFor": "1726946480", - "messageHash": "32948247c695a2545f9b35c040a293f1c6cd300062e9d7abdf0b3ed2a7b596d1", - "signature": "50346a31ad859f211294496e01083dcb85803bb27923b8d256756c71bdbfe36e1e89741215f58fd4bb8db42f04775303e60748b99f10c64e189d1f585d6b77531c", - "signer": "1b1374742cb5f84b1ef167db57236350380084e1" - } - }, - "address": "your_another_account_address" - }' - ``` - - Once these steps are completed, the ownership of the TA will be transferred. - - For more details on API requests, refer to the documentation at: - [API Documentation](https://github.com/mycel-labs/astraeus/blob/main/docs/api.md) - -8. **Sign from Transferable Account Request to API Server** - - If you own a TA and hold assets on an external chain with that TA, you can create a Tx to send assets from the TA account and broadcast it to the external chain. - - Before executing the command, make sure to set the RPC of the chain to broadcast the Tx as `WITHDRAW_TESTNET_RPC` in your `.env` file. - - Specify the arguments in the following order: the accountID of the TA you are using, the ChainID to execute the Tx, the address to which you want to send the ETH, and the amount of ETH to send. - - Example: - - ``` - $ go run scripts/utils/execute_withdraw_tx/main.go 0x439376239c54540980f027ac33e1c11a 11155111 0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3 0.0001 - ``` - - Once the transfer is successful, the `txHash` will be displayed. diff --git a/docs/develop/astraeus/getting-started.md b/docs/develop/astraeus/getting-started.md new file mode 100644 index 0000000..c13ae26 --- /dev/null +++ b/docs/develop/astraeus/getting-started.md @@ -0,0 +1,413 @@ +--- +sidebar_position: 1 +--- + +# Getting Started + +This guide will help you set up the Astraeus API server on the Suave Toliman Testnet to easily use Transferable Accounts (TA). You will learn how to create, approve, and transfer TAs using the API server. Additionally, you will learn how to sign transactions on external chains using TAs. + +### Prerequisites + +- An environment capable of running Docker +- Two accounts on the Toliman Testnet with access to their private keys and TEETH tokens +- Two accounts on the Sepolia Testnet with access to their private keys and SepoliaETH tokens + +If you do not have these tokens, you can obtain them from the [Toliman Testnet Faucet](https://faucet.toliman.suave.flashbots.net/) and [Sepolia Testne Faucet](https://www.alchemy.com/faucets/ethereum-sepolia) + +### Steps + +1. **Copy the example environment file to create your own `.env` file:** + + ``` + cp .env.example .env + ``` + +2. **Edit the `.env` file and set the `PRIVATE_KEY` to the private key of an account that holds tokens on the Suave network.** + +3. **Start the API server using Docker:** + + ``` + make run-api-server-docker + ``` + + At this point, the API server should be running locally via Docker. + +4. **Generate the Timed Signature required for API requests. Replace `your_private_key` with the private key of your account on Suave and `targetFunction` with "CreateAccount":** + + To execute a request against the API, you need to prepare a signature each time that indicates from which account and for which function the request is being made. + + ``` + go run scripts/utils/generate_timed_signature/main.go CreateAccount + ``` + + First, we are creating a signature for "CreateAccount" + + input example: + + ``` + go run scripts/utils/generate_timed_signature/main.go 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94 CreateAccount + ``` + + output example: + + ``` + "proof": { + "validFor": 1730260787, + "messageHash": "7fe8d937495fbdf3324310fddedebd0ea6a14fd9451d42b692435f4db53fbdee", + "signature": "9cc6629cbf04e3f75f2ecb2fd9b780b886975938c8b1326ad2e92ef5a705e6305c4c28b92a270950a7153f91a5607da04c0a77937dd6f4c5128864cd34a33ba21c", + "signer": "0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3", + "nonce": 3, + "target_function_hash": "030bb6482ea73e1a5ab7ed4810436dc5d10770855cdbbba0acb9a90b04852e4f" + } + ``` + +5. **Create Account Request to API Server** + + Execute the request to create a TA. Use the output from step 4 in the `proof` section: + + ``` + curl -X POST http://localhost:8080/v1/accounts -d '{ + "proof": { + "validFor": , + "messageHash": "", + "signature": "", + "signer": "", + "nonce": , + "target_function_hash": "" + } + }' + ``` + + input example: + + ``` + curl -X POST http://localhost:8080/v1/accounts -d '{ + "proof": { + "validFor": 1730260787, + "messageHash": "7fe8d937495fbdf3324310fddedebd0ea6a14fd9451d42b692435f4db53fbdee", + "signature": "9cc6629cbf04e3f75f2ecb2fd9b780b886975938c8b1326ad2e92ef5a705e6305c4c28b92a270950a7153f91a5607da04c0a77937dd6f4c5128864cd34a33ba21c", + "signer": "0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3", + "nonce": 3, + "target_function_hash": "030bb6482ea73e1a5ab7ed4810436dc5d10770855cdbbba0acb9a90b04852e4f" + } + }' + ``` + + output example: + + ``` + {"txHash":"0x98f3367e503d32d6e817ca251bc7cfefdd6d11c970fbac1bc74ad06efe7f8d49","accountId":"0x5f927be8e73951a99a84fa7b21e1d5c4","ethereumAddress":"0x6b972Cc0A1CdF473a48C27831B0A3b64CBD8E549"} + ``` + + Once the `txHash` is displayed, the account creation is complete. The displayed `accountId` and `ethereumAddress` are the ID of the account and The account's address on EVM + +6. **Deposit SepoliaETH to TA's Address** + + Once the TA is created, try sending ETH on Sepolia to the ethereumAddress held by the TA. + You can refer to the ethereumAddress held by the created TA from the result of the CreateAccount execution. + + This operation will be conducted on the Sepolia Testnet, not on the Suave Toliman Testnet, so please be careful. + +7. **Approve Address Request to API Server** + + Approve the transfer of TA ownership from the current account to another account. + + You need to create a signature to execute the ApproveAddress Function. (As with the previous step) + + ``` + go run scripts/utils/generate_timed_signature/main.go ApproveAddress + ``` + + input example: + + ``` + go run scripts/utils/generate_timed_signature/main.go 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94 ApproveAddress + ``` + + output example: + + ``` + "proof": { + "validFor": 1730262911, + "messageHash": "9978c1ecc11cd29ea7aa2d1571ef6fdba80b0501a0938d5c17ead3baa4b5dfb0", + "signature": "a1c818ebef4d29ae7d8fff29648d6aa3936d5657a03324ffa758f792a79fbca44087ce49ab485fd81c30d8da29f1b292ed744aff2071f0207385f8f44635af311b", + "signer": "0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3", + "nonce": 4, + "target_function_hash": "16d1dabab53b460506870428d7a255f9bff53294080a73797c114f4e25b5e76f" + } + ``` + + Once the signature is created, try executing the API using the signature information. + The PATH of the URL for executing the API must include the accountId of the TA you created. + + ``` + curl -s -X POST http://localhost:8080/v1/accounts//approve -d '{ + "base": { + "account_id": "", + "proof": { + "validFor": , + "messageHash": "", + "signature": "", + "signer": "", + "nonce": , + "target_function_hash": "" + } + }, + "address": "" + }' + ``` + + input example: + + ``` + curl -s -X POST http://localhost:8080/v1/accounts/0x5f927be8e73951a99a84fa7b21e1d5c4/approve -d '{ + "base": { + "account_id": "0x5f927be8e73951a99a84fa7b21e1d5c4", + "proof": { + "validFor": 1730262911, + "messageHash": "9978c1ecc11cd29ea7aa2d1571ef6fdba80b0501a0938d5c17ead3baa4b5dfb0", + "signature": "a1c818ebef4d29ae7d8fff29648d6aa3936d5657a03324ffa758f792a79fbca44087ce49ab485fd81c30d8da29f1b292ed744aff2071f0207385f8f44635af311b", + "signer": "0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3", + "nonce": 4, + "target_function_hash": "16d1dabab53b460506870428d7a255f9bff53294080a73797c114f4e25b5e76f" + } + }, + "address": "0x755201605CB3bBeE61320cc3d5Af2Bb5Ed15DE0F" + }' + ``` + + output example: + + ``` + {"txHash":"0x19eb20300738d3d2ffa63fe35cb3cf9fe1737e4d19cd65fb693b784b0abd51f5"} + ``` + + Once the `txHash` is displayed, the account approval is complete. + +8. **Transfer Account Request to API Server** + + Execute the transfer of TA ownership. This can be done by either the current TA owner or the approved account. + In this example, the transfer is executed with the signature of the TA creator, but you can also create and execute the request with the signature of the recipient. + + You need to create a signature to execute the TransferAccount Function. (As with the previous step) + + ``` + go run scripts/utils/generate_timed_signature/main.go TransferAccount + ``` + + input example: + + ``` + go run scripts/utils/generate_timed_signature/main.go 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94 TransferAccount + ``` + + output example: + + ``` + "proof": { + "validFor": 1730273730, + "messageHash": "8137a1f78f956ec8663e23bcb1f07d4739effa0254a229d3eebf3d19bb95e9e3", + "signature": "3171639eb953784b663356b60d7231cc2639d549a53b30997171f3b77f58f2271c71ff898d1993295c62775710d2c17d7fc1f135aee06be2706dcd84cacc081b1b", + "signer": "0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3", + "nonce": 5, + "target_function_hash": "29535a955f68dc291a88a89b6112c958d2edce1684117ccd6b54ca173656f65f" + } + ``` + + Once the signature is created, try executing the API using the signature information. + The PATH of the URL for executing the API must include the accountId of the TA you created. + + ``` + curl -s -X POST http://localhost:8080/v1/accounts//transfer -d '{ + "base": { + "account_id": "", + "proof": { + "validFor": , + "messageHash": "", + "signature": "", + "signer": "", + "nonce": , + "target_function_hash": "" + } + }, + "address": "" + }' + ``` + + input example: + + ``` + curl -s -X POST http://localhost:8080/v1/accounts/0x5f927be8e73951a99a84fa7b21e1d5c4/transfer -d '{ + "base": { + "account_id": "0x5f927be8e73951a99a84fa7b21e1d5c4", + "proof": { + "validFor": 1730273730, + "messageHash": "8137a1f78f956ec8663e23bcb1f07d4739effa0254a229d3eebf3d19bb95e9e3", + "signature": "3171639eb953784b663356b60d7231cc2639d549a53b30997171f3b77f58f2271c71ff898d1993295c62775710d2c17d7fc1f135aee06be2706dcd84cacc081b1b", + "signer": "0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3", + "nonce": 5, + "target_function_hash": "29535a955f68dc291a88a89b6112c958d2edce1684117ccd6b54ca173656f65f" + } + }, + "address": "0x755201605CB3bBeE61320cc3d5Af2Bb5Ed15DE0F" + }' + ``` + + output example: + + ``` + {"txHash":"0x26ad303c786550433848519411438b3f1531f568be25563d29645d1f6275341c"} + ``` + + Once these steps are completed, the ownership of the TA will be transferred. + + You can check the TA's owner address using the following command + + ``` + curl -X GET http://localhost:8080/v1/accounts/ + ``` + + example + + ``` + curl -X GET http://localhost:8080/v1/accounts/0x436d02ef904e536a870d12949e429819 + {"account":{"accountId":"0x436d02ef904e536a870d12949e429819","owner":"0x755201605CB3bBeE61320cc3d5Af2Bb5Ed15DE0F","publicKeyX":"a91cd998338946de2b6a1bc36dc109b5bcc8a826143174d7820eb0e26f05e042","publicKeyY":"90bf93a068d2dc9aa497cb25e006f14e1abfc754935d1d98c8e5a8b8d0812e64","signatureAlgorithm":"SignatureAlgorithm_ECDSA","isLocked":false}} + ``` + +9. **Unlock the Transferable Account** + + After the transfer of the TA is complete, let's perform the unlock operation from the account that received the TA. + By unlocking the TA, you will be able to withdraw assets from addresses on different chains associated with the TA. + + You need to create a signature to execute the TransferAccount Function. (As with the previous step) + Please note that the private key of the account that received the TA on the Toliman Testnet will be required. + + ``` + go run scripts/utils/generate_timed_signature/main.go UnlockAccount + ``` + + input example: + + ``` + go run scripts/utils/generate_timed_signature/main.go 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94 UnlockAccount + ``` + + output example: + + ``` + "proof": { + "validFor": 1730276042, + "messageHash": "4dc2219469c3081b806b29d942e94c09aa949726d6a043c459e091a47fac5d09", + "signature": "b23824ac0edf74ba1b5771531d20ce3b549eba74976f955cb41484b88965fe432a3da3a37a22cc38c850e38819f52a38b24ee292fc32c62fb301fe96f48d668d1c", + "signer": "0x755201605CB3bBeE61320cc3d5Af2Bb5Ed15DE0F", + "nonce": 0, + "target_function_hash": "062e71868bb32b076e90fa8fa0fa661f47d2f38ee0e9db39a5ab5569589f6332" + } + ``` + + Once the signature is created, try executing the API using the signature information. + The PATH of the URL for executing the API must include the accountId of the TA you created. + + ``` + curl -s -X POST http://localhost:8080/v1/accounts//unlock -d '{ + "base": { + "account_id": "", + "proof": { + "validFor": , + "messageHash": "", + "signature": "", + "signer": "", + "nonce": , + "target_function_hash": "" + } + } + }' + ``` + + input example: + + ``` + curl -s -X POST http://localhost:8080/v1/accounts/0x5f927be8e73951a99a84fa7b21e1d5c4/unlock -d '{ + "base": { + "account_id": "0x5f927be8e73951a99a84fa7b21e1d5c4", + "proof": { + "validFor": 1730276042, + "messageHash": "4dc2219469c3081b806b29d942e94c09aa949726d6a043c459e091a47fac5d09", + "signature": "b23824ac0edf74ba1b5771531d20ce3b549eba74976f955cb41484b88965fe432a3da3a37a22cc38c850e38819f52a38b24ee292fc32c62fb301fe96f48d668d1c", + "signer": "0x755201605CB3bBeE61320cc3d5Af2Bb5Ed15DE0F", + "nonce": 0, + "target_function_hash": "062e71868bb32b076e90fa8fa0fa661f47d2f38ee0e9db39a5ab5569589f6332" + } + } + }' + ``` + + output example: + + ``` + {"txHash":"0x26ad303c786550433848519411438b3f1531f568be25563d29645d1f6275341c"} + ``` + + You can check the TA's lock status using the following command + + ``` + curl -X GET http://localhost:8080/v1/accounts//locked + ``` + + example + + ``` + curl -X GET http://localhost:8080/v1/accounts/0x436d02ef904e536a870d12949e429819/locked + {"result":false} + ``` + +10. **Sign from Transferable Account** + + If you own a TA and hold assets on an external chain with that TA, you can create a Tx to send assets from the TA account and broadcast it to the external chain. + + Before executing the command, make sure to set the RPC of the chain to broadcast the Tx as `WITHDRAW_TESTNET_RPC` in your `.env` file. + + Specify the arguments in the following order: the accountID of the TA you are using, the ChainID to execute the Tx, the address to which you want to send the ETH, and the amount of ETH to send. + + Please note that the private key of the account that received the TA on the Toliman Testnet will be required. + + ``` + $ go run scripts/utils/execute_withdraw_tx/main.go + ``` + + When the transaction is ready, you will be prompted to confirm whether to proceed, as shown in the example below. If everything looks good, type ‘y’ and press Enter. + + input example: + + ``` + $ go run scripts/utils/execute_withdraw_tx/main.go 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94 0x439376239c54540980f027ac33e1c11a 11155111 0x0A772258e2f36999C6aA57B2Ba09B78caF7EbAd3 0.0001 + 2024/10/31 15:12:11 Using PRIVATE_KEY: 10c62a6364b1730ec101460c871952403631adb66fe7e043914c7d0056ca8e94 + 2024/10/31 15:12:11 Using RPC_URL: https://rpc.toliman.suave.flashbots.net + 2024/10/31 15:12:11 Using TA_STORE_CONTRACT_ADDRESS: 0xEa8a6ce7098B79Bd8B4920120734a5081046C44F + 2024/10/31 15:12:12 Get Account Response: account:{account_id:"0x436d02ef904e536a870d12949e429819" + owner:"0x755201605CB3bBeE61320cc3d5Af2Bb5Ed15DE0F" + public_key_x:"a91cd998338946de2b6a1bc36dc109b5bcc8a826143174d7820eb0e26f05e042" + public_key_y:"90bf93a068d2dc9aa497cb25e006f14e1abfc754935d1d98c8e5a8b8d0812e64" signature_algorithm:SignatureAlgorithm_ECDSA} + 2024/10/31 15:12:12 TA's Ethereum Address: 0x6a06c9e34f0e167c2aD2Ba3d97D588BaA9A0C37c + 2024/10/31 15:12:13 Suggested Gas Price: 4297882348 + 2024/10/31 15:12:13 Suggested Gas Tip Cap: 305482713 + 2024/10/31 15:12:13 Transaction Hash: c3f2f2779693bb9687ea25f1d8585f7907031fbbbfc57492296c94d3cf246f33 + 2024/10/31 15:12:17 Sign Response: tx_hash:"0x74322f8c27019b1ce5c475cf76af225c47cc7a97ef4afb73cba4f68526dab132" signature:"323ab432a41b47a8e02383a0648262248850b84c9d5530394252de87e0869baa7944c6db4b50c2eacc6562bc85ec824eb0451ac6a6adac19b1ec42bcfe84bcd101" + 2024/10/31 15:12:17 Sender Address: 0x6a06c9e34f0e167c2aD2Ba3d97D588BaA9A0C37c + You need to depoist send value & gas ETH to 0x6a06c9e34f0e167c2aD2Ba3d97D588BaA9A0C37c (y/n): + ``` + + Once the transfer is successful, the `txHash` will be displayed. + + output example + + ``` + 2024/10/31 15:12:19 Transaction sent successfully! Tx Hash: 0xf833ec2ea7202e929cf440b935702df8b56e24a2b4cea03d776f2d9357d2d7f8 + ``` + + Let’s check the outputted Tx Hash on each chain’s explorer. + + - [Sepoilia Testnet Etherscan](https://sepolia.etherscan.io/) + + For more details on API requests, refer to the documentation at: + [API Documentation](https://github.com/mycel-labs/astraeus/blob/main/docs/api.md) From 96ffd0ff121ff1dc8db6b55e2883c2c86390f5ea Mon Sep 17 00:00:00 2001 From: peaceandwhisky Date: Fri, 1 Nov 2024 12:38:35 +0900 Subject: [PATCH 2/2] Revert the file name --- docs/develop/astraeus/{getting-started.md => get-started.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/develop/astraeus/{getting-started.md => get-started.md} (100%) diff --git a/docs/develop/astraeus/getting-started.md b/docs/develop/astraeus/get-started.md similarity index 100% rename from docs/develop/astraeus/getting-started.md rename to docs/develop/astraeus/get-started.md