Building in public

Tech skills needed

Hard skillCompetency 1 - 5Fluency 1 - 5
The Graph
Dune Analytics10
Blockchain data API1
GitHub Action11

Week 1 24/1 - 29/1


highly recommended Patrick free code camp 32 hr tutorial (opens in a new tab), let's start with that. other recommended Austin Griffiths on YouTube (opens in a new tab)


  • bored watching tutorial and following along. Not sure how much I'm picking up. I'll try to do the tutorial without looking at the solution and see how far I get
  • create a simple storage contract


-[x] Write a contract that receives a minimum usd amount of eth and allows only the contract creator to withdraw. -[x] write from scratch w/o looking at solution


  • Write a contract that receives a minimum usd amount of eth and allows only the contract creator to withdraw
  • Think about what NPM packages I can build
  • Think about what tutorial I can build

NPM package ideas

  1. Simplified web3 library abstracting complex aspects of web3.js
  2. NPM module for creating tokens, nft's, dapps, etc
  3. NPM module for creating a gated community allowing sismo badge holders access
  4. DID protocol with key requirement variables
  5. create a token gated community website generator e.g. disqus

Tutorial ideas

  1. create a token gated community website
  2. create a token gated community website generator

Notes from today

  • using ethers to create an encrypted json key from .env file link (opens in a new tab)
  • using process.env.PRIVATE_KEY with key in .env file or key sent in cmd execution e.g. PRIVATE_KEY=MYPRIVATEKEY node index.js


  • in ethers - write the storage contract
  • in ethers - write a contract that receives a minimum usd amount of eth and allows only the contract creator to withdraw

Notes from today

  • using solc to compile contracts from .sol to .bin + .abi to deploy with ethers const factory = new ethers.ContractFactory(abi, binary, wallet)
  • needed specific version i.e. yarn add solc@0.8.7-fixed
  • yarn solcjs --bin --abi --include-path node_modules/ --base-path . -o . SimpleStorage.sol
  • add this to your scripts file e.g. scripts: {"compile": "yarn...... SimpleStorage.sol"}
  • ethers, a provider is the network node, a signer is the private key of the wallet that deploys the contract, contract is the contract obj
  • wasting time on the typescript tutorial, I'll change to javascript


  • if running npx hardhat doesn't return the hardhat creation prompts, check for parent hardhat.config.js file and remove it or run npx hardhat --verbose
  • hardhat uses built in network by default => config in hardhat.config.js e.g. module.exports = {defaultNetwork: "hardhat"}
  • or execute via cmd yarn hardhat run scripts/deploy.js --network hardhat
  • yarn hardhat brings up tasks e.g. run or custom task
  • yarn hardhat console brings up the js console to execute
  • open zeppelin ref (opens in a new tab)


  • execute specific tests with yarn hardhat test --grep update-when-test
  • execute specific test by changing it("should... to it.only("should...
  • tests use beforeEach to execute the contract creation before calling each it
describe('SimpleStorage', function () {
  let SimpleStorageFactory, simpleStorage;
  beforeEach(async function () {
    simpleStorageFactory = await ethers.getContractFactory('SimpleStorage');
    simpleStorage = await simpleStorageFactory.deploy();
  it('should start with a favorite number 0', async function () {
    const currentValue = await simpleStorage.retrieve();
    console.log(`current value is ${currentValue}`);
    const expectedValue = '0';
    assert.equal(currentValue.toString(), expectedValue);
  it('should update when we store a number', async function () {
    const valUpdated = 5;
    const res = await;
    await res.wait(1);
    const valReturned = await simpleStorage.retrieve();
    console.log(` -------- ${valReturned}`);
    assert.equal(valUpdated, valReturned);


Notes from today

  • really good github readme link (opens in a new tab)
  • hardhat typescript support requires yarn add --dev @types/mocha @types/node @typechain/hardhat @typechain/ethers-v5 ts-node typechain typescript
  • Typescript needs correct typing on contracts, typechain and typechain/hardhat creates ts bindings automatically
    • yarn hardhat typechain

Week 1 wrap up

  • focused too much on getting through the tutorial and not enough practice
  • writing 1 contract and working through hardhat won't improve competency / fluency on solidity. Need to dedicate more chunked time to practice individual skills rather than watching a tutorial with everything

Week 2 30/1 - 5/2

Skills to practice

Hard skillCompetency 1 - 5Fluency 1 - 5


  • complete up to hour 10 of the tutorial without looking at the solution
  • find how I can improve fluency on TypeScript and Solidity


complete the below w/o github copilot link (opens in a new tab)

-[x] create the SimpleStorage contract that stores a fav number, retreives the number, and stores an array of person structs (name, favnum) -[x] create a hardhat deployment script to deploy the SimpleStorage contract on Goerli -[x] verify it on Etherscan in the code -[x] interact with it -[x] Deploy the contract to hardhat network to view transactions on the console chain -[x] Deploy the contract to the Goerli testnet and verify it on Etherscan -[x] create a task to check the block number -[x] create tests for the contract -[x] check with coverage -[x] work with gas reporter in tests, check usd price with coinmarketcap api -[x] update to Typescript

round 1 completed - 5 hours

round 2 completed - 3 hours


  • Why does return HardhatError: HH303: Unrecognized task verify: verify but run doesn`t.
const verify = async (contractAddress, args) => {
  await'verify: verify', {});

Changing learning strategy

  1. watch tutorial while following along and pausing (1.5 hr tutorial)
  2. try to replicate

Proposed strategy

  1. skip over whole tutorial quickly
  2. watch tutorial in 10-15 minute segments
  3. replicate each 10-15 segment before moving forwards
  4. repeat


  1. change from deployment script to hardhat-deploy
  2. copy the Aave format for helper-hardhat-config to enable different chain deployment.
  3. update contract constructor and variables for different chain deployment
  4. create a mock chainlink contract for hardhat network
  5. use tags to deploy on different networks
  • make the constructor dynamic for the pricefeed address (if different chains required) e.g.
AggregatorV3Interace public priceFeedAddress;
constructor(address _priceFeedAddress) { priceFeedAddress = AggregatorV3Interface(_priceFeedAddress); }
  • add tags to deployment script to test / deploy different networks

  • test the mock deployment in hardhat with the mock chainlink contract module.exports.tags = ["all", "mocks"] yarn hardhat deploy --tags mocks

  • code style in solidity details (opens in a new tab)


notes from today

  • testing with ethereum waffle
  • transaction test cases with Arrange, Act, Assert pattern
  • debugging objects with javascript debugger
  • console.log in solidity with hardhat console.log import "hardhat/console.sol";
  • process.exit(0)) to exit the script succesfully. process.exit(1) to exit with error

Storage (gas optimization), current link (opens in a new tab)

  • constant and immutable variables are stored in bytecode (a pointer to )
  • variables are stored in storage (a pointer to the blockchain)
  • gas storage details found here (opens in a new tab)
  • prepend s_ to variables appending to storage. e.g. uint256 s_favNum; (because storage is expensive)
  • rather than having public s_favNum variable, create a public function to return the value. e.g. function getFavNum() public view returns (uint256) { return s_favNum; }
  • rather than throwing an error with text e.g. require(msg.sender == owner, "sorry, you can't do that");, use an error code with revert =>
error FundMe__NotOwner();
modifier errorOrNot(){
  if (msg.sender != i_owner) revert FundMe__NotOwner();


Time to create my own contract from something I was thinking of, a community with members, members and friend each other.

draft 1

contract CommunityMember {
  //make friend
  //make post w price
  //like post - can't like own post
  //buy post from someone
  //remove friend
contract ASingleCommunity {
    //add member
    //set membership payable
    //remove member
    //get list of members
    //make group
    //make group admin
    //transfer group admin
contract AllCommunities {
    //new member
    //remove member
    //get list of members
    //mapping member address and member name => mapping(address => string)
    //get list of friends - mapping address to name => mapping(address => mapping(address => ))
    //create community
    //get a community
    //get all communities

draft 2 (simplify)

contract CommunityMember {
  //make friend
  //remove friend
  //make post - add to post map, convert string to hex
  //like post - can't like own post, add to like map
  //------non essential
  //buy post from someone
contract AllCommunities {
    //new member
    //remove member
    //get list of members -- mapping member address and member name => mapping(address => string)
    //get list of a members friends -- mapping address to name => mapping(address => mapping(address => ))
    //------non essential
    //get a list of posts of 1 member - mapping address to posts => mapping(address => mapping(contentAddress => ))
    //get a list of post likes of 1 member - mapping address to likes => mapping(address => contentAddress)



What's not working and why

Types containing (nested) mappings can only be parameters or return variables of internal or library functions.

  • the Member can't be stored in memory because it has a mapping
  • mapping is not allowed in memory because map locations are stored at random in storage details (opens in a new tab)
  struct Member {
      address memberAddress;
      string name;
      address[] friends;
      mapping(address => bool) friendsMap;
  function getMemberByAddress(address _i) mustBeMember public view returns(Member memory){
        uint256 memberPosition = membersMap[_i];
        return membersArray[memberPosition];


    struct MemberMatch {
        uint256 arrayPos;
        bool memberExists;
    mapping(address => MemberMatch) public amembersMap;
    or this
    mapping(address => uint256) internal membersMap;
    mapping(address => bool) internal memberExists;

Week 2 wrap up

  • spent 3 days on the community contract, amongst other things
  • need to have a faster learning velocity to improve
  • plan for next week = find a way to increase learning velocity on contracts & typescript

Week 2 30/1 - 5/2

Skills to practice

Hard skillCompetency 1 - 5Fluency 1 - 5

Week 3: 6/2 - 12/2

7/2 deploy my community contract with hardhat


  • contracts
  • scripts / deploy.ts
  • test Community.ts
  • hardhat config, add etherscan, add solidity compiler, add typechain, add gas reporter, add coverage, add waffle, add chai, add dynamic chain support

I didn't

  1. wait for 5 block confirmations before verifying
  2. create a utils file for verification
  3. use hardhat deploy
  4. use solhint
  5. didn't create a task


  1. starting leetcode for 4 hrs a day to increase learning velocity on typescript and solidity
  2. finding a way to contribute to sismo



  • Community project in hardhat
  • Read Sismo docs

notes from the day

  • use solhint => yarn solhint contracts/Community.sol. solhint provides error messages you see in remix via the command line
  • moving to hardhat deploy starts here (opens in a new tab)

Error message Property 'attach' is missing in type 'ContractFactory any[], BaseContract' but required in type 'Communityfactory'.ts(2741) Communityfactory.ts(189, 12): 'attach' is declared here.

What does this mean? deployer = (await getNamedAccounts()).deployer

  • using the getNamedAccounts function from hardhat-deploy
  • returns a promised object with named accounts


  • I found leetcode to include bad questions which I wasted hours on. Algoexpert has better quality questions and I'm going to use that instead
  • I signed up for my Masters in CS but not sure I'm doing the right thing. Orientation was boring
  • Need to write a 2 page thesis proposal for my Masters in CS



TS [](2 (opens in a new tab) number sum) round1 1st time = 3 hours

  1. brute force - double nested for loop
  2. make map, loop through array and check if target - current number is in map e.g. current = 3, target = 5, 5 - 3 = 2, check if 2 is in map, if it is, return [3, 2], if not, add 3 to map
  3. sort and use 2 pointers



[](2 (opens in a new tab) number sum) round 2 2nd time = 2 hours

  1. my 2nd array loop starts back at 1. why? I'm not sure but I should base the 2nd loop iterator on the first i.e. j = i + 1
  2. was fine
  3. while loop was wrong. I was checking for successful match and then incrementing pointers. My while loop should check for invalid, incremenet pointers inside then check for valid again outside while

[](subsequence (opens in a new tab) validation) round 1 1st time = 1.5 hours



[](2 (opens in a new tab) number sum) round 3 3rd time = 45 minutes

  1. easy
  2. easy
  3. while loop was wrong and got stuck. While loop again checked for successful match (same mistake as yesterday)... I should check (while left pointer is smaller than right)

[](subsequence (opens in a new tab) validation) round 2 1st time = 1 hour

  • spent most of the time iterating from array pointer rather than sequence pointer


  1. Look into Sismo & zk privacy preserving protocols to understand how to contribute
  2. Look into Twitter API
  • disorganized "looking", will do again tomorrow with more structure e.g. hypothesis driven research rather than bottom up



[](2 (opens in a new tab) number sum) round 3 3rd time = 15 minutes

  1. easy
  2. easy
  3. easy

[](subsequence (opens in a new tab) validation) round 3 3rd time = 10 minutes

[](tournament (opens in a new tab) winner) round 1 1st time = 45 minutes

[](q1 (opens in a new tab) assessment) round 1 1st time = 1 hour. I am tired and spent too long deciding what data structure I'll use.



  1. plan for day (AM/PM)
  2. day notes (AM/PM)
  3. day review + tomorrow plan
  4. update twitter


  1. plan for week in relation to competency/fluency towards web3 TS eng + 3 axis approach
  2. review week


This is a crazy solution

  • doesn't take the BST property into account (left is smaller than root, right is bigger than root)
  • doesn't take into account that the BST is sorted




-[x] TS practice, Solidity practice -[x] create mock API for Sismo (needs improving) (opens in a new tab) (opens in a new tab) (opens in a new tab)

-[x] (opens in a new tab) -[x]complete both recursive and iterative solutions -[] (opens in a new tab)


  • sismo
  • uni


What's the difference between calling return on a recursive function and not calling return?

  • Return uses the results of the function in the next iteration of the function.
  • Not calling with return could instead for example, update a global var
  • Think about the factorial (1 x 2 x 3 x 4 x 5)
function factorial(n: number): number {
  if (n === 0) {
    return 1;
  } else {
    return n * factorial(n - 1);
const result: number = factorial(5);
console.log(result); //120
function printNumbers(n: number): void {
  if (n === 0) {
  } else {
    printNumbers(n - 1);
  • in branch sums, summing the BST branches, I don't need to return the bstRecurse because I'm not using the result of the sum in the next iteration (I'm just adding the sum and adding it to the sum array)
  • in find closest value in BST, I'm using the running sum of each bst search to compare it to the proceeding sums.

Given a class

class BinaryTree {
  value: number;
  left: BinaryTree | null;
  right: BinaryTree | null;
  constructor(value: number) {
    this.value = value;
    this.left = null;
    this.right = null;
  1. create an interface for a node
  2. create an array of nodes using spread operator
  3. pop a node object off and destructure it
//1 => each node is either the class left or right and needs `BinaryTree | null` otherwise error =  Type 'BinaryTree | null' is not assignable to type 'BinaryTree'.
interface Node {
  node: BinaryTree | null;
  depth: number;
let nodes: Node[] = [
  { node: tree, depth: 0 },
  { node: tree.left, depth: 1 },
  { node: tree.right, depth: 1 },
let stack: Node[] = [];
let { node, depth } = stack.pop()!; /// without the ! I get Property 'node' does not exist on type 'Node | undefined'. because we define the Node as being a tree or null.

logic returns errors

For the Binary Tree class above, the following function returns Function lacks ending return statement and return type does not include 'undefined'. Object is possibly 'null'.

function recurseHandler(tree: BinaryTree | null, depthSum: number): number {
  if (tree === null) {
    return (
      depthSum +
      recurseHandler(tree.left, depthSum + 1) +
      recurseHandler(tree.right, depthSum + 1)
  • function lacks ending return statement because there's nothing after the if statement
  • I'm saying that tree is null, but then I'm trying to access tree


function recurseHandler(tree: BinaryTree | null, depthSum: number): number {
  if (tree === null) return 0;
  return (
    depthSum +
    recurseHandler(tree.left, depthSum + 1) +
    recurseHandler(tree.right, depthSum + 1)





  • better work in the morning than afternoon. I was easily distracted in the PM.
  • I'll start working out around midday and going to the library in the afternoon to continue focus


difference between forEach and for...of

  • for...of is preferred for iterating arrays of objects
  • for...of can break out of a loop and stop processing the rest of the array
  • forEach can only return which stops processing the current iteration and moves to the next element
// for...of
const: numbers[]: number[] = [ 1, 2, 3];
for (const number of numbers) {
  if (number === 2) break;
// forEach
numbers.forEach((number) => {
  if (number === 2) return; // error

Difference between

export interface ResultMetadata {} and export type ResultMetadata = {}

  • Interface can be extended by other interfaces and classes, types can't be extended e.g.
interface A {
  a: string;
interface B extends A {
  b: string;

how to typecast

interface PersonFirst {
  name: string;
  age: number;
interface PersonSecond {
  name: string;
  age: number;
const personFirst: PersonFirst = { name: 'Alice', age: 30 };
const personSecond = personFirst as PersonSecond; // using the as keyword
// or
const person2 = <Person>myObj; // using the angle bracket syntax

how to use type guards (opens in a new tab) (opens in a new tab)


How does the type work here (opens in a new tab)?

type DeepPartial<T> = {
  [P in keyof T]?: DeepPartial<T[P]>;
function find<T>(criteria: DeepPartial<T>): T[] {
type User = {
  id: number;
  name: string;
  address: {
    country: string;
    city: string;
    house: string;
    zipcode: string;
const users = find({
  address: { coutry: 'UK' }



  • TS practice, Solidity practice


-[x] create static generator with API key for Sismo

  • needed to execute process.env.MY_API_KEY as terminal variables e.g. MY_API_KEY=1234 yarn generate-group ...
  public constructor() {
    this.url = '';
    this.restProvider = new RestProvider();
    this.headers = {
      'x-dune-api-key': process.env.MY_API_KEY as string,       //both work but the second is better
      'x-dune-api-key': DuneAnalyticsProvider.getAPIKey(),




  • TS practice, solidity practice


  • Sismo dune provider handling execution wait time


Truthy vs Non Truthy

Non Truthy

  • bool('') = false
  • bool(0) = false
  • bool(-0) = false
  • bool(NaN) = false
  • bool(null) = false
  • bool(undefined) = false
  • bool(false) = false
  • false or null when coerced into a boolean value using the ! (not) operator

if(myString) vs while(myString) vs while(!!myString)

  • let myString = ""
  • if(myString) is falsy in a boolean contex because the value of myString is an empty string
  • while(myString), is evaluated differently. It's not checking whether the value of myString is truthy or falsy, it's checking if the value myString exists. Even if it exists as an empty string, it is truthy because it exists
  • while(!!myString) is checking the value of myString the same as if(myString)
  • This is called "coercing" a value into a boolean value


!!"" = false
!!"hello" = true // compiles to true = true because the string is type set to a bool
let myString = 'hello';
while (myString) {
//hello, ello, llo, lo, o, undefined
while (!!myString) {
//hello, ello, llo, lo, o, ended

getting stuck on recursive problems, writing here my instructions for next time recursion

  1. Start with the base case
  • What stops the algo?
  • What's the simplest thing that can stop it?
  • Is my Algo going to reach it or run indefinately?
  1. What are the smaller sub-problems?
  2. how does the recursive step combine the solutions into the original problem?
  3. if stuck, draw a recursive tree
def factorial(n):
    if n == 0:
        return 1
        return n * factorial(n - 1)

factorial(4) | |--> factorial(3) | | | |--> factorial(2) | | | | | |--> factorial(1) | | | | | | | |--> factorial(0) | | | | | | | | | |--> return 1 | | | | | | | |--> return 1 | | | | | |--> return 2 | | | |--> return 6 | |--> return 24

why use <T> instead of <any> in a generic function?

export async function http<T>(request: RequestInfo): Promise<T> {
  const response = await fetch(request);
  const body = await response.json();
  return body;
// example consuming code
interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
const data = await http<Todo[]>('');
  • <T> is a type param. the caller can specify what type they use and what to expect back
  • in the http function example above, the API consumer calls await http<Todo[]>(...) and expects an array of Todo objects to be returned from the API endpoint
  • <T> is generally better than <any> because it provides stronger type safety and better code readability
  • use <T> when you know the type of data returned from the API
  • <any> is only better when the response type is unknown, or if you need to typecast it to a different type



  • TS practice, sprint to the end of AlgoExpert level 1 26/26 questions. Then I'll cover ground on Solidity and then move onto level 2
  • Sismo TS dune client


  • Sismo TS dune client
  • Cryptography 101 study


  • Binary search tree -> I iterated from the start through to the end, rather than recursive search or iterator with moving window left and right. I need to work on it conceptually
  • 2 ways to search, 1 is break the array in the middle and recursive search the left and right. time is O(log n) because you're cutting the array in half each time. Space is O(log n) because you're creating a new array each time.
  • The other way is a moving window, while left < right, get the middle number, if the target is smaller than middle, move the window to the left. Time is (log n) and space is O(1) because you're not creating a new array each time.


  • Sismo TS dune client When making POST/GET requests, does Sismo want to use private functions? Does Sismo use string https links for API or use query concatentaion? How do we handle the dune column names e.g. HB Hello 1 Should we only return the address array or should we return the whole object? I've abstracted the return type, is that ok?


What's going on here? Why do we need to return the Promise.resolve(fetchResult) rather than just

function feedingA<T>(string: fetch): Promise<T> {
  const fetchResult = await fetch(string);
  return a<T>(Promise.resolve(fetchResult));
function a(fetchResult: Promise<Response>) {
  return fetchResult.then((res) => res.json());


Error message

const ErrorClass = *stateErrorMap[state]*

Element (stateErrorMap[state]) implicitly has an 'any' type because expression of type
can't be used to index type '{
  QUERY_STATE_FAILED: typeof ExecutionFailedError
  QUERY_STATE_EXPIRED: typeof ExecutionFailedError
  QUERY_STATE_CANCELLED: typeof ExecutionFailedError; }
Property 'QUERY_STATE_EXECUTING' does not exist on type '{
  QUERY_STATE_FAILED: typeof ExecutionFailedError;
  QUERY_STATE_EXPIRED: typeof ExecutionFailedError;
  QUERY_STATE_CANCELLED: typeof ExecutionFailedError; }


if (state in stateErrorMap) {
  const ErrorClass = stateErrorMap[state];
  throw new ErrorClass(state);
const stateErrorMap = {
  QUERY_STATE_FAILED: ExecutionFailedError,
  QUERY_STATE_EXPIRED: ExecutionFailedError,
  QUERY_STATE_CANCELLED: ExecutionFailedError,



find 3 largest numbers in an array, return them without sorting the array

  • initially I initialized the 3 top numbers to 0, but that doesn't work if the array has negative numbers
  • i solved this by either 1) initializing the numbers to null or 2) initializing the numbers to the first 3 numbers in the array or null
  1. let top1 = null; -> I don't think this is good TypeScript because it's not a number. I can't do math on it. I think it should be let top1: number | null = null;
  2. let sortedArr: Array<number | null> = [null, null, null]

Bubble sort array of numbers

  • while loop with a for loop inside
  • while loop is false, iterate from beginning to end of array -1 -count (array -1 because we're comparing the current number with the next number, -count because each time we loop around while we bubble a top number to the top of the array)
  • if the current number is greater than the next number, swap them



insertion sort

  • iterate array from 1 to end. (1 because )


AlgoExpert Selection sort

Quick recap When to use while loop vs iterator loop

  • Iterator's are good when you know the number of iterations in advance
  • While loops are good when you don't know the number of iterations in advance

Combining for and while loops Why put a while loop inside a for loop? Why put a for loop inside a while loop?

//reverse a string


Algoexpert selection sort

2 methods

  1. 2 loops,
    1. iterate from 0 to end of array, find the smallest number and swap it with the current number which is position 0
    1. iterate from 1 to end of the array, find the smallest number and swap it with the current, which is position 1
  1. 1 loop, 1 while loop
    1. while pointer < end of array -1 (because we're comparing to pointer2)
    1. if pointer2 (starting at pointer1 + 1), set that pointer to the smallest number, then swap the smallest number with pointer1

Palindrome check

3 methods

  1. left and right pointers. 2 pointers, 1 at the start, 1 at the end. while left < right, if left !== right, return false. else return true
  2. reverse the string and compare it to the original string
  3. recurse through the string, if the first and last letter are the same, recurse through the string without the first and last letter. if the string is empty, return true. else return false

Ceaser Cipher (shift string x number of times)

  1. iterate through the string, if the character is a letter, shift it. Either
      1. convert the character to a number, shift it, convert it back to a character
      1. create a map of the alphabet, shift the character, return the character
      1. create an array of the alphabet characters, use indexOf to get the position of the input. Add the key amount to the position and modulus it to get the shifted position, return the shifted position character

quick checklist to help me with algorithms

    1. what is the input? what is the output?
    1. pseudo code explaining how to do it
    • what are the pointers and comparators?
    • what are the bounds?
    • what would make a variable undefined?
    • are there different ways to do it?
    1. time and space complexity
    1. code the framework, log the pointers and comparators
    1. code the logic


Algoexpert return true if chars in string a available in string b (target) and correct number of times

  • 3 methods
  1. iterate through string a, for each character, get the number of times it appears in both strings. return true if it's the same
    • time complexity. n = number of characters in target. m = number of characters in stringa. O(n * (m + n)) = O(n^2 + nm) = O(nm)
    • space complexity. O(1) because we're not storing anything
  1. use a map or dictionary to store the number of times each character appears in string a. iterate through string b to check.
    • time complexity. O (n + m) because we're iteratig through both strings
    • space complexity.

Difference between dictionary and map

const dictionary: { [key: string]: number } = {};
dictionary.a = 1;
dictionary['b'] = 2;
dictionary.a += 1;
dictionary['b'] += 1;
let a1 = dictionary.a;
let b2 = dictionary['b'];
delete dictionary['b'];
const map = new Map<string, number>();
//set & get Update needs getting and setting
map.set('a', 1);


Algoexpert return an array of strings with semordnilap pairs

array = ['cat', 'dog', 'tac', 'god', 'act'];
//response = [['cat', 'tac'], ['dog', 'god']]
//create a set from the array items
const set = new Set(array);
//check if the reverse of the item is in the set
//declare a result array
const pairs: [string, string][] = [];
//iterate through....

Why use a Set over an Array or Map?

  • set has iteratable methods
  • set has keys needing to be unique
  • can use set.add() and set.has() to check if an item is in the set

Array's dont have keys or unique values Map's have a key value pair. Set's dont have a key value pair, only keys. So use a Set if you don`t need the value

Reversing a string

//reverse it with a function
const reverse = (s: string) => {
  let r = '';
  for (let i = s.length - 1; i >= 0; i--) r += s[i];
  return r;
const reversedString = s.split('').reverse().join('');

I've now completed the beginner section of Algoexpert

  • before moving to the medium section, to test my fluency, I want to do all questions in 1 day
  • I'll choose the recursive questions first, I don't feel confident in them yet
  • Things I'd like to focus on
    • better understand when to use while loop, for loop, for of loop, for in loop
    • better understand edge conditions on looping e.g. when to use -1, when to loop backwards from end to start
    • better understand time and space complexity
    • comparing previous coding solution to current, comment on it
    • focus on consise & readability e.g. use arrow functions and ternary operators

Fetch review, explaining to myself

What's the difference between this:

  .dune(queryId, queryParameters)
  .then((duneData) => {
    for (const row of duneData) {
      formattedData[row[addressFieldName]] = 1;
    console.log(`formattedData: ${formattedData}`);
    if (formattedData.undefined === 1)
      throw Error(
        'No data returned. Check the addressFieldName matches the address field in Dune. The address field in Dune must not have a space e.g. ETH Address. If so, rename it to ETH_Address or ETHAddress'
  .catch((error) => console.log(`error: ${error}`));

and this:




Recalling my goals for repeating the easy section

  • be precise in why I'm selecting a while loop, for loop, for of loop, for in loop
  • be precise in determining edge conditions on looping e.g. when to use -1, when to loop backwards from end to start
  • better understand time and space complexity
  • use arrow functions and ternary operators
  • recursion - understand when to use return recurse and when to just use inline function
const fletcher3 = async <T>(): Promise<T> => {
  const res = await fetch(`${url3}`, {
    method: 'GET',
    headers: {
      'x-dune-api-key': DUNE_API_KEY,
  const dat = await res;
  return dat;
  // const data = await res.json();
  // return data;
Type 'Response' is not assignable to type 'T'.
  'T' could be instantiated with an arbitrary type
  which could be unrelated to 'Response'

This is saying dat is of type Response and I can't send an already typed Type to <T>because it wants a more generic type Response has a json() method and other methods, so I can't just return dat, I need to return dat.json()

I can typecast it to T like (return dat as T) but I shouldn't because it's not generic. I should just return dat.json()


6/3 - 13/3


  • better understand when to use while loop, for loop, for of loop, for in loop
  • better understand edge conditions on looping e.g. when to use -1, when to loop backwards from end to start
  • better understand time and space complexity
  • focus on consise & readability e.g. use arrow functions and ternary operators


Finish dune analytics provider & tutorial


applied cryptography course usyd



closest val in BST

  • Time O(log(n)) best because it's a BST. O(log(n)) for worst because our target could be the last item
  • Space(O(1)) because we're not storing much
  • Finding a single number so we can return the recursive function (not just call it?)



  • continue recursion problems on AlgoExpert easy
  • follow this to improve my recursion game