Last active
April 7, 2025 15:46
-
-
Save KBPsystem777/6789d7f31758ee36b9a3a670b018961b to your computer and use it in GitHub Desktop.
BitDev's Thanksify Smart Contract
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.19; | |
contract Thanksify { | |
address public owner; | |
struct Message { | |
string text; | |
address sender; | |
uint256 timestamp; | |
} | |
mapping(address => Message[]) public messagesByUser; | |
event MessageSaved(address indexed sender, string message, uint256 timestamp); | |
constructor() { | |
owner = msg.sender; | |
} | |
/// @dev Insert the saveMessage function here | |
function getMyMessages() public view returns (Message[] memory) { | |
return messagesByUser[msg.sender]; | |
} | |
} |
π Useful links
Useful links for this activity
β½ Sonic - BitDev Testnet faucet Request link: https://testnet.soniclabs.com
π Verification API Key: VUZWZHBYWDSI2GCS49CH9WI485KW3XP47Z
π£ Feedback form (We'd love to hear your thoughts): bit.ly/devup-exp
π¦ DevUp! Socials
Web3 Developers Philippines Facebook Group: bit.ly/bitdev-fb
Sonic CodeCamp Cheatsheet
Sonicscan API key: 9IVTYAG8HTK97KENV8VII4K5K9MSQ32QYF
How to deploy and verify your NFT smart contract in Sonic Blaze Testnetwork?
1. Update your hardhat.config.js
file loacted in π \packages\hardhat\hardhat.config.ts
and paste the code below.
import * as dotenv from "dotenv";
dotenv.config();
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-ethers";
import "@nomicfoundation/hardhat-chai-matchers";
import "@typechain/hardhat";
import "hardhat-gas-reporter";
import "solidity-coverage";
import "@nomicfoundation/hardhat-verify";
import "hardhat-deploy";
import "hardhat-deploy-ethers";
import { task } from "hardhat/config";
import generateTsAbis from "./scripts/generateTsAbis";
// If not set, it uses ours Alchemy's default API key.
// You can get your own at https://dashboard.alchemyapi.io
const providerApiKey = process.env.ALCHEMY_API_KEY || "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF";
// If not set, it uses the hardhat account 0 private key.
// You can generate a random account with `yarn generate` or `yarn account:import` to import your existing PK
const deployerPrivateKey =
process.env.__RUNTIME_DEPLOYER_PRIVATE_KEY ?? "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
// If not set, it uses our block explorers default API keys.
const etherscanApiKey = process.env.ETHERSCAN_MAINNET_API_KEY || "DNXJA8RX2Q3VZ4URQIWP7Z68CJXQZSC6AW";
const etherscanOptimisticApiKey = process.env.ETHERSCAN_OPTIMISTIC_API_KEY || "RM62RDISS1RH448ZY379NX625ASG1N633R";
const basescanApiKey = process.env.BASESCAN_API_KEY || "ZZZEIPMT1MNJ8526VV2Y744CA7TNZR64G6";
const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
// https://docs.soliditylang.org/en/latest/using-the-compiler.html#optimizer-options
runs: 200,
},
},
},
],
},
defaultNetwork: "sonicBlaze",
namedAccounts: {
deployer: {
// By default, it will take the first Hardhat account as the deployer
default: 0,
},
},
networks: {
sonicBlaze: {
url: "https://rpc.blaze.soniclabs.com",
accounts: [deployerPrivateKey],
},
// View the networks that are pre-configured.
// If the network you are looking for is not here you can add new network settings
hardhat: {
forking: {
url: `https://eth-mainnet.alchemyapi.io/v2/${providerApiKey}`,
enabled: process.env.MAINNET_FORKING_ENABLED === "true",
},
},
},
// configuration for harhdat-verify plugin
etherscan: {
apiKey: `${etherscanApiKey}`,
customChains: [
{
network: "sonicBlaze",
chainId: 57054,
urls: {
apiURL: "https://api-testnet.sonicscan.org/api",
browserURL: "https://testnet.sonicscan.org",
},
},
],
},
// configuration for etherscan-verify from hardhat-deploy plugin
verify: {
etherscan: {
apiKey: `${etherscanApiKey}`,
},
},
sourcify: {
enabled: false,
},
};
// Extend the deploy task
task("deploy").setAction(async (args, hre, runSuper) => {
// Run the original deploy task
await runSuper(args);
// Force run the generateTsAbis script
await generateTsAbis(hre);
});
export default config;
2. Paste this code on your networks.ts
file located under π \packages\nextjs\utils\scaffold-eth\networks.ts
import { defineChain } from "viem";
import * as chains from "viem/chains";
import scaffoldConfig from "~~/scaffold.config";
type ChainAttributes = {
// color | [lightThemeColor, darkThemeColor]
color: string | [string, string];
// Used to fetch price by providing mainnet token address
// for networks having native currency other than ETH
nativeCurrencyTokenAddress?: string;
};
export type ChainWithAttributes = chains.Chain & Partial<ChainAttributes>;
export type AllowedChainIds = (typeof scaffoldConfig.targetNetworks)[number]["id"];
const sonicBlaze = defineChain({
id: 57054,
name: "Sonic Blaze Testnet",
nativeCurrency: {
decimals: 18,
name: "Sonic",
symbol: "S",
},
rpcUrls: {
default: { http: ["https://rpc.blaze.soniclabs.com"] },
},
blockExplorers: {
default: {
name: "Sonic Testnet Explorer",
url: "https://testnet.soniclabs.org/",
},
},
testnet: true,
});
export const extendedChains = {
...chains,
sonicBlaze,
};
// Mapping of chainId to RPC chain name an format followed by alchemy and infura
export const RPC_CHAIN_NAMES: Record<number, string> = {
[chains.mainnet.id]: "eth-mainnet",
[chains.goerli.id]: "eth-goerli",
[chains.sepolia.id]: "eth-sepolia",
[chains.optimism.id]: "opt-mainnet",
[chains.optimismGoerli.id]: "opt-goerli",
[chains.optimismSepolia.id]: "opt-sepolia",
[chains.arbitrum.id]: "arb-mainnet",
[chains.arbitrumGoerli.id]: "arb-goerli",
[chains.arbitrumSepolia.id]: "arb-sepolia",
[chains.polygon.id]: "polygon-mainnet",
[chains.polygonMumbai.id]: "polygon-mumbai",
[chains.polygonAmoy.id]: "polygon-amoy",
[chains.astar.id]: "astar-mainnet",
[chains.polygonZkEvm.id]: "polygonzkevm-mainnet",
[chains.polygonZkEvmTestnet.id]: "polygonzkevm-testnet",
[chains.base.id]: "base-mainnet",
[chains.baseGoerli.id]: "base-goerli",
[chains.baseSepolia.id]: "base-sepolia",
[chains.celo.id]: "celo-mainnet",
[chains.celoAlfajores.id]: "celo-alfajores",
};
export const getAlchemyHttpUrl = (chainId: number) => {
return scaffoldConfig.alchemyApiKey && RPC_CHAIN_NAMES[chainId]
? `https://${RPC_CHAIN_NAMES[chainId]}.g.alchemy.com/v2/${scaffoldConfig.alchemyApiKey}`
: undefined;
};
export const NETWORKS_EXTRA_DATA: Record<string, ChainAttributes> = {
[chains.hardhat.id]: {
color: "#b8af0c",
},
[chains.mainnet.id]: {
color: "#ff8b9e",
},
[chains.sepolia.id]: {
color: ["#5f4bb6", "#87ff65"],
},
[chains.gnosis.id]: {
color: "#48a9a6",
},
[chains.polygon.id]: {
color: "#2bbdf7",
nativeCurrencyTokenAddress: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
},
[chains.polygonMumbai.id]: {
color: "#92D9FA",
nativeCurrencyTokenAddress: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
},
[chains.optimismSepolia.id]: {
color: "#f01a37",
},
[chains.optimism.id]: {
color: "#f01a37",
},
[chains.arbitrumSepolia.id]: {
color: "#28a0f0",
},
[chains.arbitrum.id]: {
color: "#28a0f0",
},
[chains.fantom.id]: {
color: "#1969ff",
},
[chains.fantomTestnet.id]: {
color: "#1969ff",
},
[chains.scrollSepolia.id]: {
color: "#fbebd4",
},
[chains.celo.id]: {
color: "#FCFF52",
},
[chains.celoAlfajores.id]: {
color: "#476520",
},
};
/**
* Gives the block explorer transaction URL, returns empty string if the network is a local chain
*/
export function getBlockExplorerTxLink(chainId: number, txnHash: string) {
const chainNames = Object.keys(chains);
const targetChainArr = chainNames.filter(chainName => {
const wagmiChain = chains[chainName as keyof typeof chains];
return wagmiChain.id === chainId;
});
if (targetChainArr.length === 0) {
return "";
}
const targetChain = targetChainArr[0] as keyof typeof chains;
const blockExplorerTxURL = chains[targetChain]?.blockExplorers?.default?.url;
if (!blockExplorerTxURL) {
return "";
}
return `${blockExplorerTxURL}/tx/${txnHash}`;
}
/**
* Gives the block explorer URL for a given address.
* Defaults to Etherscan if no (wagmi) block explorer is configured for the network.
*/
export function getBlockExplorerAddressLink(network: chains.Chain, address: string) {
const blockExplorerBaseURL = network.blockExplorers?.default?.url;
if (network.id === chains.hardhat.id) {
return `/blockexplorer/address/${address}`;
}
if (!blockExplorerBaseURL) {
return `https://etherscan.io/address/${address}`;
}
return `${blockExplorerBaseURL}/address/${address}`;
}
/**
* @returns targetNetworks array containing networks configured in scaffold.config including extra network metadata
*/
export function getTargetNetworks(): ChainWithAttributes[] {
return scaffoldConfig.targetNetworks.map(targetNetwork => ({
...targetNetwork,
...NETWORKS_EXTRA_DATA[targetNetwork.id],
}));
}
3. Lastly, update your scaffold.config.ts
file under π \packages\nextjs\scaffold.config.ts
import { defineChain } from "viem";
import * as chains from "viem/chains";
export type ScaffoldConfig = {
targetNetworks: readonly chains.Chain[];
pollingInterval: number;
alchemyApiKey: string;
walletConnectProjectId: string;
onlyLocalBurnerWallet: boolean;
};
export const DEFAULT_ALCHEMY_API_KEY = "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF";
// Create a Custom Sonic Blaze Network Definition
const sonicBlaze = defineChain({
id: 57054,
name: "Sonic Blaze Testnet",
nativeCurrency: {
decimals: 18,
name: "Sonic",
symbol: "S",
},
rpcUrls: {
default: { http: ["https://rpc.blaze.soniclabs.com"] },
},
blockExplorers: {
default: {
name: "Sonic Testnet Explorer",
url: "https://testnet.soniclabs.org/",
},
},
testnet: true,
});
const scaffoldConfig = {
// The networks on which your DApp is live
targetNetworks: [sonicBlaze, chains.hardhat, chains.sepolia, chains.baseSepolia],
// The interval at which your front-end polls the RPC servers for new data
// it has no effect if you only target the local network (default is 4000)
pollingInterval: 30000,
// This is ours Alchemy's default API key.
// You can get your own at https://dashboard.alchemyapi.io
// It's recommended to store it in an env variable:
// .env.local for local testing, and in the Vercel/system env config for live apps.
alchemyApiKey: process.env.NEXT_PUBLIC_ALCHEMY_API_KEY || DEFAULT_ALCHEMY_API_KEY,
// This is ours WalletConnect's default project ID.
// You can get your own at https://cloud.walletconnect.com
// It's recommended to store it in an env variable:
// .env.local for local testing, and in the Vercel/system env config for live apps.
walletConnectProjectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || "3a8170812b534d0ff9d794f19a901d64",
// Only show the Burner Wallet when running on hardhat network
onlyLocalBurnerWallet: true,
} as const satisfies ScaffoldConfig;
export default scaffoldConfig;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
π Save Message Function: