Skip to content

Instantly share code, notes, and snippets.

@jerryan999
Last active July 26, 2023 18:17
Show Gist options
  • Save jerryan999/56ffce6571cfd8ef06e96d580b252b32 to your computer and use it in GitHub Desktop.
Save jerryan999/56ffce6571cfd8ef06e96d580b252b32 to your computer and use it in GitHub Desktop.
How to Get Smart Contract Creation Block Time
package main
import (
"context"
"fmt"
"log"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
type ContractFinder struct {
client *ethclient.Client
latestBlock int64
}
func NewContractFinder(provider string) (*ContractFinder, error) {
conn, err := ethclient.Dial(provider)
if err != nil {
return nil, err
}
latestBlock, err := conn.BlockByNumber(context.Background(), nil)
if err != nil {
return nil, err
}
return &ContractFinder{
client: conn,
latestBlock: latestBlock.Number().Int64(),
}, nil
}
func (c *ContractFinder) codeLen(contractAddr string, blockNumber int64) int {
ctx := context.Background()
data, err := c.client.CodeAt(ctx, common.HexToAddress(contractAddr), big.NewInt(blockNumber))
if err != nil {
log.Fatal("Whoops something went wrong!", err)
}
return len(data)
}
func (c *ContractFinder) GetContractCreationBlock(contractAddr string) int64 {
return c.getCreationBlock(contractAddr, 0, c.latestBlock)
}
// use binary search to find the block number where the contract was created
func (c *ContractFinder) getCreationBlock(contractAddr string, startBlock int64, endBlock int64) int64 {
if startBlock == endBlock {
return startBlock
}
// fmt.Println("contractAddr", contractAddr, "startBlock", startBlock, "endBlock", endBlock)
midBlock := (startBlock + endBlock) / 2
codeLen := c.codeLen(contractAddr, midBlock)
if codeLen > 2 {
return c.getCreationBlock(contractAddr, startBlock, midBlock)
} else {
return c.getCreationBlock(contractAddr, midBlock+1, endBlock)
}
}
type Data struct {
address string
startBlock int64
}
func worker(cttFinder *ContractFinder, contractChan <-chan Data, resultChan chan<- Data) {
for data := range contractChan {
creationBlock := cttFinder.GetContractCreationBlock(data.address)
resultChan <- Data{data.address, creationBlock}
}
}
func main() {
cttFinder, err := NewContractFinder("https://mainnet.infura.io/v3/[YOURKEY]")
if err != nil {
log.Fatal("Whoops something went wrong!", err)
}
contracts := []string{
"0x3a4f40631a4f906c2bad353ed06de7a5d3fcb430",
"0xa4991609c508b6d4fb7156426db0bd49fe298bd8",
}
inputChan := make(chan Data)
resultChan := make(chan Data)
// write the task into input chan
go func() {
defer close(inputChan)
for _, adr := range contracts {
inputChan <- Data{adr, 0}
}
}()
// start 4 workers
var wg sync.WaitGroup
numberWorkers := 1
for i := 0; i < numberWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
worker(cttFinder, inputChan, resultChan)
}()
}
go func() {
wg.Wait()
close(resultChan)
}()
for r := range resultChan {
fmt.Println(r)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment