Build on SanChain
Which smart contracts do I need to use?
If you are going write some scripts, then you probably want to use the Signals or Predictions contracts on the Santiment Chain.
Where is smart contracts code, audits and documentation?
Here is a documentation for smart contracts:
https://github.com/santiment/sanr-contracts/tree/main/documentation.
Here is the smart contracts code:
https://github.com/santiment/sanr-contracts/tree/main/contracts.
Here is the last audit result for the subscription contract:
Hacken_Santiment_SCA_Santiment_Subscription_Mar2024.
Smart contract addresses and ABIs
All our smart contracts are deployed on three chains: Ethereum Mainnet, Sanrchain and zkSync.
The link below contains all smart contract names and addresses:
https://api.sanr.app/v1/smarts
Contract names contain a prefix which can help you detect where the contract was deployed. L1 means that the contract was deployed to Ethereum Mainnet, L2 means - to Sanrchain, and Zk means - to ZkSync.
First address in the array is a current version, the others are the previous versions.
To get the contract ABI you should replace the contract address in the end of this link (instead of 0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098):
https://api.sanr.app/v1/abi/0x7C5A0CE9267ED19B22F8cae653F198e3E8daf098.
For example:
import fetch from "node-fetch";
import { ether } from "ethers";
const wallet = new ethers.Wallet(
"0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709",
new ethers.JsonRpcProvider("https://sanrchain-node-stage.santiment.net")
// todo change to https://sanrchain-node.santiment.net
); // todo delete private key from docs
async function getSanrAPIResponse(path, method = "get", body, accessToken) {
// 'https://sanrnew-api.production.internal.santiment.net/api/v1/'
const url = "https://sanr-api.stage.internal.santiment.net/api/v1/";
const options = {
method,
headers: { "Content-Type": "application/json" },
};
if (body) options.body = JSON.stringify(body);
if (accessToken) options.headers.Authorization = `Bearer ${accessToken}`;
const response = await fetch(url + path, options);
return await response.json();
}
async function getSanrContract(name) {
const smarts = await getSanrAPIResponse("smarts");
const address = smarts["L2" + name][0];
const abi = await getSanrAPIResponse("abi/" + address);
return new ethers.Contract(address, abi, wallet);
}
const signalsContract = await getSanrContract("Signals");
How to get (and spend) gas?
In Sanrchain testnet in order to get 0.000001 gas on your address, you should just register and connect your crypto wallet into https://sanr.app. This amount should be more than enough, since the gas price is extremely low for the testnet.
To be sure that it is enough, you can set these parameters for each transaction in Sanrchain:
const gasParams = {
maxFeePerGas: 2000,
maxPriorityFeePerGas: 4,
};
In zkSync mainnet you can use the following links to get some gas:
https://app.rhino.fi/bridge?token=ETH&chainOut=ZKSYNC&chain=ETHEREUM
Code examples
Creating a signal
Let’s start with signal creating. First you need to decide which signal type you are going to use. You can also create a new signal type if it does not exist:
async function createNewSignalType() {
const tx = await signalsContract.createDataType(
"MyAwesomeDataType(address someAddress,uint256 someValue)",
gasParams
);
const committedTx = await tx.wait();
console.log("Type ID: ", committedTx.logs[0].args[0]);
return committedTx.logs[0].args[0];
}
const myTypeID = await createNewSignalType();
Then let’s make a hash (https://eips.ethereum.org/EIPS/eip-712#rationale-for-encodedata) from your signal data and send that hash to the contract:
function getMySignalTypeDataHash(someAddress, someValue) {
return ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "address", "uint256"],
[
ethers.id("MyAwesomeDataType(address someAddress,uint256 someValue)"),
someAddress,
someValue,
]
)
);
}
async function createNewSignal() {
const tx = await signalsContract.createSignal(
myTypeID,
getMySignalTypeDataHash("0x35aDcF84f44A7348889fF8445aFD164723837b3b", "13"),
gasParams
);
const committedTx = await tx.wait();
console.log("Signal ID: ", committedTx.logs[0].args[2]);
return committedTx.logs[0].args[2];
}
createNewSignal();
Creating a prediction
If you wanna see your prediction on our https://sanr.app/ site and if you expect that our oracles will open or close your prediction you should use exactly this way to create directionHash parameter. This is especially true for the way to create salt.
const predictionsContract = await getSanrContract("Predictions");
function getDirectionDataHash({ salt, isDirectionUp }) {
return ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["bytes32", "bytes32", "bool"],
[ethers.id("Info(bytes32 salt,bool isDirectionUp)"), salt, isDirectionUp]
)
);
}
async function authoriseOnSanR() {
const { code } = await getSanrAPIResponse("auth");
const signedMessage = await wallet.signMessage(code);
const profile = await getSanrAPIResponse("auth", "post", {
signedMessage,
originalMessage: code,
isMobileUser: true,
});
return profile.accessToken;
}
async function createNewPrediction() {
const accessToken = await authoriseOnSanR();
const symbol = "BTC/USD";
const entropy = ethers.id(
crypto.getRandomValues(new Uint32Array(3)).join("")
);
const directionInfo = {
salt: ethers.id(entropy + ethers.id(accessToken)),
isDirectionUp: true,
};
const predictionParams = {
issuer: wallet.address,
symbol: ethers.encodeBytes32String(symbol),
entropy,
directionHash: getDirectionDataHash(directionInfo),
stopLossPrice: 0,
takeProfitPrice: 0,
expectedTopPrice: ethers.parseEther("9999999"),
expectedBottomPrice: 0,
expectedTime: ethers.parseEther("9999999"),
openPrice: 0,
leverage: 1,
openWhenPriceMoreThen: false,
};
const tx = await predictionsContract.newPrediction(
...Object.values(predictionParams),
gasParams
);
const committedTx = await tx.wait();
return committedTx.logs[0].args[2];
}
createNewPrediction();