chatGPT, Grok, Gemini와 같은 거대 인공지능들은 우리 일상의 모든 영역을 혁신적으로 변화시키고 있다. 하지만 우리 일상 속 대부분의 AI는 "중앙집중형(Centralized)"이라는 한계 속에서 powerful entity들에 의해 선별되고 컨트롤되는 정보에 의존하고 있다.
하지만 이러한 AI들이 정말 "전적으로" 우리가 의존해도 되는 존재일까.
Microsoft, Google, NVIDIA와 같은 거대 기술 기업들이 막대한 데이터, 컴퓨팅 인프라를 독점하다시피 하고 있는 현재 상황에서, 작은 스타트업들은 이들과 제대로 된 경쟁조차 하기 힘들다. 이는 단순 경제적 문제를 넘어 혁신의 다양성이 저해되고, 소비자들의 선택권 또한 제한되며 이로 인해 AI의 발전 속도 자체를 늦추는 결과를 낳고 있다.(출처)
하지만, 더욱 심한 문제는 편향성의 고착화이다. 중앙집권적으로 학습된 AI 모델들은 개발자들의 세계관, 그리고 편향된 데이터가 사용되었을 경우, 이를 그대로 반영하게 된다. 유네스코의 2024년 연구에 따르면, 주요 LLM들이 정치, 인종, 성별 등에 대해 편향된 결과를 연관시키는 것으로 나타났다. (출처)
많은 사람들이 업무, 과제, 일상 대부분에서 AI와 함께하는 사회 분위기 속에서, 이렇게 중앙화되어 선별되고, 통제되는 AI는 우리의 생각과 삶의 방향에도 지대한 영향을 미칠 수 있을 것이다. 이러한 중앙집권형 AI의 한계를 뛰어넘기 위해 필요한 것이 바로 탈중앙화된 AI(DeAI)이다.
지금 소개할 Allora 역시 이러한 문제의식에서 출발한 DeAI중 하나이면서, 아주 매력적이라는 생각이 들어 소개해본다.
alloralabs.xyz
1. 창립 배경
처음에 Upshot이라는 이름으로 창립되었던 이 회사는, AI를 활용한 자산 가격 발견에 중점을 둔 회사였지만, AI와 크립토 인프라 구축 분야에서 꽤 긴 시간동안 경험을 쌓으면서 더 큰 비전을 품게 되었다.
2024년 2월, 회사 이름을 Allora Labs로 리브랜딩하면서 탈중앙화 AI 네트워크 구축이라는 더욱 큰 목표를 잡아 AI의 패러다임을 바꾸겠다는 의지를 표현하였다.
창립자는 Nick Emmons(CEO)와 Kenny Peluso(CTO)가 있다.
CEO인 Nick은 John Hancock의 Lab of Forward Thinking에서 수석 블록체인 엔지니어로 근무하였으며, CTO인 Kenny또한 John Hancock에서 full-stack개발자로 일한 경험이 있다.
이들은 이와같이 전통 금융업계에서 경험을 통해 중앙집권적 시스템이 가진 효율성, 그리고 그 한계를 직접 경험하였으며, 탈중앙화된 해결책의 필요성을 느낀 배경이 되었다.
2. 성장 과정
이들은 2020년 초 100만 달러 시드 라운드를 시작으로, 2021년 750만, 2022년 2,200만 달러의 series-A 라운드를 성공적으로 완료했으며, 2024년에는 추가로 300만 달러의 투자를 유치해 총 3,500만 달러의 펀딩을 달성했다.
투자자 라인업 또한 인상적인데, Allora 웹사이트에서 확인할 수 있듯, Polychain Capital, Framework Ventures, Blockchain Capital 등 블록체인, AI 분야 최고 투자사들이 참여한 것을 볼 수 있다.
3. Allora Labs의 DeAI
이들은 단순한 AI플랫폼을 만들고자 하는 것이 아니라, World Machine Intelligence Abstraction Layer를 만들어 AI를 널리 접근 가능하게 만들고, 데이터, 알고리즘을 가진 누구나 AI에 기여할 수 있는 생태계를 만들고자 한다.
아래는 이들이 진행하고 있는 여러 프로젝트들의 일부이다.
- 1. Mind Network의 FHE기술을 응용해 민감/학습 데이터가 암호화되어 프라이버시를 유지할 수 있도록 한다.
- 2. 일본 gumi와 협력을 통해, 게임 내 AI 기반 추론, 에이전트 등을 지원하고 있다.
- 3. Story Protocol과 IP 생태계 통합을 통해 지능형 IP를 관리 및 창작물의 가치평가를 제공하고 있다.
- 4. AgentiPy라는 Python framework를 통해 AI agent들이 블록체인과 원활히 상호작용할 수 있도록 하여 DeFAI분야에서
4-1. Allora의 가격 예측을 활용해 Meteora에서 유동성 조정을 통해 수수료 earning & impermanent loss 최소화 4-2. Orca에서 Allora의 실시간 가격 Intelligence를 이용해 스마트 거래 실행
이러한 연구를 하고 있다.
- 5. 학습 데이터와 알고리즘을 제공하는 사용자들에게 블록체인 네트워크를 통한 차별화된 인센티브를 제공하고 있으며,
이외에도금융분야, 헬스케어, 게이밍 등 여러 분야에서 범용성 있게 적용되고 있는 상황이다.
Allora Network는 단순히 예측을 집계하는 것이 아닌, 현재 상황에서 각 모델이 얼마나 정확할지를 예측하는 예측(Forecasting the Forecasting)을 통해 더 강력한 clustered-result를 생성한다. 이는 기존 AI 시스템들과 완전히 다른 접근법으로, 개별 모델의 한계를 뛰어넘는 탈중앙화된 방식을 활용한 진정한 집단지성을 구현한다고 볼 수 있다.
이러한 접근법을 통해 Allora Labs는 AI의 미래가 소수 기업의 독점이 아닌, 전 세계 개발자들과 연구자들이 협력하여 만들어가는 집단지성의 시대를 만들고자 하는 것 같다.
제작한 circuit에 최적화된 data를 생성하는 단계. Powers of Tau에서 생성된 데이터를 바탕으로, 각 circuit의 constraint를 만족하기 위한 proving key, verification key를 생성하는 단계. R1CS를 GQP로 변환하는 과정도 포함된다.
- r1cs 파일을 이용해 data를 생성함 - proving key: Prover가 zkp생성할 때 필요한 key - verification key: verifier가 zkp를 검증하는 데 필요한 key - proving key, verification key ∈ upside.zkey
어떤 방법을 사용한 것일지, 코드를 통해 분석해보자. 컨트랙트 원본을 찾을 수 없어 bytecode를 dedaub을 통해 decompile하였다. swap이 이루어질 때 swapExactTokensForTokens() 함수를 통해 swap이 이루어지며, 대부분 sushiswap의 pair, router 컨트랙트에서 로직을 처리한다. 스캠코인이 동작에 영향을 주는 부분은 transfer()함수를 호출하는 과정에서 영향을 주게 된다.
dedaub을 통해 decompile한 결과를 보고 어떠한 방법으로 진행되는지, 살펴보자.
1. transfer()함수에서 0xc08로 jmp
2. 0xc08에서…
recipient, msg.sender 주소 0인지 체크 (그렇다면 revert)
0x12f9로 jmp. 인자에는
_getRoleAdmin[0].field0[msg.sender]
amount
recipient
msg.sender를 넣는다.
아래 부분은 일반적인 transfer의 동작과 같다. (erc20:_update()와 같다고 하는게 맞으려나?)
3. 0x12f9를 보면 ...
stor_0_0_19와 msg.sender주소를 비교하는 것이 보인다.
해당 주소를 검색해보니, sushiswap의 pair address가 나온것을 확인하였다. 즉, pair가 transfer를 할 때의 로직과, 일반 사용자가 transfer를 할 때 로직에 차이가 있는 것이다. 아무래도 AMM이다보니, 유동성풀에서 거래를 하려면 pair는 자유롭게 거래를 하도록 해야하니.. 이렇게 한 것으로 보인다. 좀 더 들여다보자.
if문으로 들어와서 첫 번째 if문은 _getRoleAdmin[0].field0[msg.sender]의 결과가 0일 경우에 들어오는 조건문이다(if(!varg0)). 해당 _getRoleAdmin[0].field0[msg.sender] 변수는 어떻게 세팅되는것인지를 찾아보면 아래와 같다.
즉, grantRole함수를 통해 0x73a로 jmp를 하고, (아마 grantRole권한을 확인하려는 듯 하다) role을 부여할 자격이 있는지 확인한 후 role을 부여한다.
이처럼, msg.sender에게 특정 role이 있는지를 확인한 뒤, role이 있다면 그저 통과, 아니라면 if문에 들어간다.
owner_1[varg3].field_1[varg3]은 0으로 세팅되어있으니, varg1(amount)를 통해 조작해보면,
최근 Base 맥시가 된 것 같다. Base, 솔라나쪽에서 AI관련 섹터가 상당히 핫한 것 같은데, Base에서 떠오르는 AI-agent들이 어떠한 것이 있는지, 그리고 어떻게 사용하는지 알아보고자 Virtuals Protocol에 대한 공부를 해보려 한다. 이를 위해 Virtuals Protocol Whitepaper를 읽어보며 AI라는 토픽의 현위치와 기술적 이해를 해보고자 한다.
글을 쓰기 시작하는 시점에서, Virtuals에 대해 아는 것은AI-agent 관련 token에서의 펌프펀이라는 것만 알고 있다.
Virtuals Protocol은 Game, Entertainment 분야의 AI agent를 블록체인을 통해 ‘공동소유’할 수 있도록 하는 플랫폼이며, AI-agent를 통해 자율적으로 운영하고 다양한 application에서 수익창출이 가능한 자산으로 변환한다.
대부분의 프로젝트는 어떠한 Pain Point에서 시작한다. Virtuals는 어떠한 것을 해결하기 위해 출시되었을까.
Virtuals Protocol이 해결하고자 하는 세 가지 Pain Point
Complexity in implementing AI agents into consumer applications
> Plug&Play 방식으로 소비자 application이 AI-agent를 간편하게 사용할 수 있도록 함
Lack of revenue for AI finetuners and dataset contributors
> contributor에 대한 수익 분배를 제공하도록 함
Limited access for non-AI experts to capitalize on AI agent opportunities
> 비 전문가도 AI-agent에 대한 소유, 참여를 가능하게 함
그래서 Virtuals는 무엇을 하고자 하는가?
AI를 “수익을 창출하는 자산”으로 간주하여, 사용자들이 이에 투자하여 지분을 갖고, 공동소유할 수 있도록 한다.
그렇다면, 어떻게 수익을 창출한다는 것일까?예를 들어 Virtuals에서 가장 잘나가는 agent 중 하나인 $AIXBT의 경우, 분석서비스를 제공하면서 이를 통해 데이터 판매 수익, 서비스 이용료 등을 거두며, 또한 소셜 활동을 통해 yaps 포인트를 쌓아 수익을 창출하는 활동을 한다.
Whitepaper의 설명을 조금 더 읽어보면, 도파민을 뿜어내는 컨텐츠의 무제한 생산을 통해 사용자들을 모으고, 맞춤화된 컨텐츠 생산을 통해 끊임없는 도파민 분비를 촉진하겠다는 의지를 보이고 있다. 즉, 더 많은 컨텐츠, 초개인화를 통해 사용자당 평균 수익, 그리고 유지율의 기하급수적 증가를 하고자 한다. (도파민이라는 표현이 있긴 하지만, 자극적인 컨텐츠 뿐 아니라, $AIXBT의 경우와 같이 지식, 데이터를 통한 도파민까지 포괄하는 내용으로 해석할 수 있다.)
AI-agent를 Game, Entertainment 분야에서 수익창출을 하도록 한다는 설명이 있었다. 즉, 로블록스, 틱톡 등 플랫폼에서 가상의 인플루언서 활동을 통해 수익을 창출할 수 있도록 한다는 이야기이다.
사용자들은 이를 통해 누구나 AI-agent를 소유하고, 개발에 참여할 수 있으며, 이를 통한 인센티브를 공정하게 분배할 수 있다.
그래서 Virtual Agents가 뭔데?
스스로 학습하고, 계획하고, 결정을 내리는 “자율적인” agent.
3D 공간에서 활동
물리적, 디지털 방식으로 상호작용
자체 지갑을 통한 트랜잭션 생성
이러한 활동을 할 수 있다고 하는데, 잘 와닿지는 않는다. 계속 자세히 알아보자.
Agent
G.A.M.E 프레임워크를 이용해 자율 AI-agent를 만들 수 있다. player(사람, agent 모두를 포함)는 각종 상호작용을 할 수 있으며, 이러한 상호작용 안에서 새로운 연쇄반응이 유발된다. 이를 통해 결과적으로 각 플레이어가 고유한 스토리라인을 경험할 수 있게 된다.
모든 player들과의 상호작용에 대한 진행 과정은 Virtuals에 의해 기록이 되며, 이를 통해 더 깊고 지속적인 UX를 제공한다.
Agent의 두 가지 종류인 IP Agent와 Functional Agent에 대해 알아보자.
IP Agents
원래 IP는 Intellectual Property를 뜻하며, 지식재산, 혹은 지식재산권을 이야기한다.
Virtuals에서는, 특정 캐릭터를 나타내는 가상의 agent로, 사람들이 친숙하게 느낄 수 있는 대상을 기반으로 한다.
Frog, Meme, Trump, Vitalik, Anime 등
이들은 사용자들과의 정서적 연결을 위해 설계되며, 게임, 소셜, 엔터 등에서 흥미를 유도한다.
사용 예시: 가상 인플루언서, 게임NPC, 교육, 이벤트, 마케팅 등
Functional Agents
IP Agent를 서포트하는 agent로, 기능 중심의 작업을 수행하여 UX 최적화를 목표로 한다.
데이터분석, 데이터 처리, 환경분석, 자동화 등
IP Agent가 프론트엔드라면, Functional Agent는 백엔드라고 생각하는 것이 좋다.
사용 예시: AI-agent간 통신, 작업, 알고리즘 실행, player와 상호작용 지원
즉, IP agent는 스토리와 엔터테인먼트 / Functional Agent는 기술적 지원과 UX에 초점을 두고있다고 볼 수 있음.
대표적인 IP Agent인 LUNA와, Functional Agent인 G.A.M.E을 통해 살펴보자.
블록체인, Web3라는 세계에 입문한지 어느덧 1년이라는 시간이 되었다. 처음에는 블록체인 기술이라는 것을 정말 “탈중앙화된 금융” 그 이상도, 이하도 아닌 것으로 인식하고 있었다. 때문에 이더리움, 솔라나와 같은 모든 종류의 코인을 그저 비트코인과 대동소이한 암호화폐 라고만 생각했다. 하지만, 온체인 상에 어플리케이션을 올리고, 탈중앙화된 방식으로 서비스를 운영하며, 꽤나 high-level의 프로그래밍 언어도 존재하고, 해킹 방식 또한 기존에 접하던 것들과는 다소 차이가 있는, 흔히 말하는 2세대 블록체인의 여러 요소들이 나에게 꽤나 매력적으로 다가왔던 것 같다. 일년간의 경험을 통해 나의 미래먹거리는 Web3라는 생각을 굳힐 수 있었고, 블록체인밸리 학회를 시작으로, 회사 인턴십을 거쳐 업사이드 아카데미를 지난 시점의 내가 crypto씬에서 1년동안 어떠한 공부를 했는지, 그 행적을 다시금 돌아보고자 2024 crypto와 관련된 기억들을 적어본다.
사실 2023 3~4분기정도부터 적게될 것 같다.
crypto에 대한 내용이기 때문에 암호화폐와 암호시스템에 대한 내용을 모두 적어보려 한다 ㅎㅎ
Blockchain Valley
blockchain valley 로고
고려대학교 블록체인 학회인 블록체인밸리(줄여서 블밸이라고 부른다).
2023년 3분기가 시작할 무렵까지 내가 정보보안, 더 나아가 IT라는 분야에서 어느 부분에 자신이 있는지 긴가민가했다. 시스템해킹, 암호학, 리버싱, 개발 모두 공부해보았지만 내 적성에 과연 맞는지, 또는 남들보다 내가 잘 할 수 있는 분야인지에 대한 확신이 부족했다. 그러던 중 여름방학이 되어 학교 커뮤니티에 각종 학회, 동아리 공고가 올라오는것들을 눈팅하고 있었는데, 그 중 가장 내 눈에 띈 것이 블밸 신입 학회원 모집 공고였다. 위에서도 언급했듯, 나는 블록체인 기술을 탈중앙화된 금융 정도의 개념으로만 받아들이고 있었는데, 공고에는 리서치팀, 개발팀을 따로 나누어 뽑는 것을 보고 ‘엥? 블록체인에서.. 개발..?’이러한 의문이 들었다. 화폐 시스템 위에서 개발을 한다니, 이게 무슨 말인가. 이러한 의문에서 시작된 서칭을 통해 처음 알게 된 것이 바로 DApp 즉, 블록체인상에서 동작하는 Application이었다. 이건 대체 뭘까.. 라는 막연한 흥미를 가지고 정말 아무것도 모른채로 지원했는데,, 정말 운좋게도 합격을 해 블밸 4기 멤버로 활동을 할 수 있었다.
블록체인 밸리에서 나는 블록체인, 더 나아가 Web3라는 새로운 생태계를 맞이하였다. 난생 처음 메타마스크를 설치해 개인 웹지갑을 생성하고, 업비트를 설치해 이더리움을 전송하는 등 정말 걸음마 단계부터 시작했던 것 같다. 더 나아가 Web3생태계에 존재하는 여러 구성요소(Defi, DAO 등), 메인넷(Ethereum Mainnet)에 대해 공부하고 여러가지 프로젝트를 만들어보는 등 새로운 것을 계속해서 쌓는 과정이 상당히 즐거웠다. 하지만 ‘보안’을 공부하는 나에게는 딱 fit하지 않다는 생각이 들었는데, 그 이유는 1) 개발 과정에 맞추어진 커리큘럼, 2) 백엔드보다 프론트엔드에 맞춰진 교육, 3) 원리를 모두 알고 넘어가는 것 보다 포트폴리오 생산에 맞추어진 것 같은 느낌. 이 세 가지 느낌을 많이 받았던 것 같다. 그래서 학회보다는 혼자만의 공부가 필요할 것 같다는 생각을 하게 되었던 것 같다. (전해듣기로 지금은 리서치, 개발, 보안 이 세 가지 팀으로 쪼개져 더 다양하고 심화된 커리큘럼이 제공되는 것 같다.)
그렇게 혼자 공부를 하면서 Uniswap 분석, EVM 분석 등 이론공부와 Ethernaut 등 워게임을 통한 Attack vector 학습 등 개인적인 공부를 진행했다. 하지만 혼자 공부를 하게 되니 계속해서 텐션도 떨어지고, 영어울렁증으로 인해 해외 자료는 찾아볼 생각도 안하는 등 여러모로 생산적인 공부를 하지는 못했던 것 같고, 사실, 당시에는 Web3에 대한 흥미가 좀 떨어졌었다.
2023년에는 이정도인 것 같은데, 이더리움, DApp, 유니스왑. 이정도 말고는 더 뭔가 공부한 느낌은 없던 것 같다. (Remix가 없으면 컨트랙트 배포를 하지 못하고, hardhat은 쓸줄도 모른다. foundry는 들어본 적도 없었다.)
LG전자 CSEU 인턴십
산학협력의 일환으로 우리 학과에 티오 4명이 제공되어 ‘설마 되겠어~’ 하고 지원했던 LG전자 인턴십. 2024년 1월~2월동안 LG전자 VS사업부에서 CSEU(Cyber Security Engineering Unit)에서 Key Management 관련 업무를 진행하였다.
이 기간동안은 잠시 Web3와 멀어져 C, C++ 기반의 KMS(키 관리 시스템) 개발에만 몰두했다.
VS사업부는 자동차 전장 관련 하드웨어 부품을 만드는 부서로, 조명, 인포테인먼트시스템 등 제품을 생산한다. 내가 속해있던 CSEU는 사이버보안 관련 개발을 하는 조직이었으며, 그중에서도 Key management 부서는 암호화 키를 관리 및 안전하게 보관하는 솔루션을 개발하는 부서였다.
나는 해당 조직에서 실제로 사용하는 제품을 개발하는 것 보다는(아마 인턴에게 제공할 수 있는 보안 수준 때문인 듯..) OS레벨에서의 message queue를 이용한 프로세스-KMS 간 key 연산 프로토콜을 개발하는 일종의 프로젝트를 진행하였다.
솔직히 결과물에 부족한 점도 많았고, 경험 부족에서 나오는 애로사항도 정말 많았지만, 요구사항 분석부터 시작하여, 프로젝트 설계, 구현 과정 설계, 테스트케이스 작성까지 여러 단계를 거치면서 정말 많은 것을 배웠다. 개발 실력을 넘어 프로젝트 설계 및 구현에서의 라이프사이클을 배운 것이 가장 컸다.
당시에 작성했던 명세. 지금 와서 다시 보니 정말 조잡하고 쏘큣 하다
KMS 개발을 하는 과정에서 key를 보관하고 안전한 연산을 수행하는 HSM, 그리고 이에 대한 표준인 pkcs#11을 공부해서 책임급 직책을 가진분들 앞에서 발표도 해보고, 실제로 암호화 통신이 작동하는 과정과 모든 요소에서 고려해야 할 각종 공격벡터에 대해서도 직접 개발해보지 않으면 알 수 없는 고려사항 등을 이해할 수 있었다. 무엇보다도, 진짜 “대기업”의 여러 사내 문화와 복지, 그리고 업무 방식 등을 이해할 수 있었던 것이 가장 귀중한 경험이었다.
그리고 얼마전에 느낀 것인데,,, 이 때 KMS-HSM간 통신, 그리고 HSM 모듈을 공부할 때의 기억이 TEE를 이용한 AI-agent에 대해 깊게 이해하는 데 많은 도움이 되었다.
함께 인턴십 과정 거친 4인방 한컷
업사이드 아카데미 1기(Dunamu x Theori)
업사이드 아카데미 로고
2월 말 인턴십을 끝내고, 또 정신없는, 많은 것을 배우는 한 학기가 시작됐다. AI보안이라는 과목을 통해 선형대수학, 통계학과 AI에 대한 이론적 바탕을 공부하였다. 또한 암호프로토콜이라는 과목을 통해 Commitment scheme, 다양한 signature, threshold signature등 암호학적 개념과, Secure MPC, ZKIP, 그리고 암호화폐에 적용된 프라이버시 기술 등 프라이버시 및 암호화폐의 기초를 이루는 개념들을 공부하였다(처음 이 수업을 들을 때는 이런 수업인줄 몰랐는데, 듣다보니 점점 암호화폐였다).
이러한 공부를 거치며 점점 Web3에 대한 흥미를 되찾기 시작했고, 그 무렵 인터넷 기사에서 두나무X티오리에서 Web3 보안 아카데미 1기 모집을 한다는 글을 보고 냅다 지원해버렸다. 서류, 실기, 면접 세 가지 전형을 거쳐 2024년 모든 경험 중 가장 값진 경험이 될 업사이드 아카데미 1기에 합격하였다.
굿즈, 맥북, 냉장고, 스낵바
RAMY볼펜, Stanley 텀블러, 두나무 굿즈, 아카데미 티셔츠 및 바람막이, 그리고 대망의 맥북 프로 M3 제공까지 미친 혜택. 그리고 무료 스낵바와 무제한 음료수를 제공하며 개인&팀별 프로젝트룸과 모니터까지 제공해주는 미친 복지까지. 두나무, 그리고 티오리가 이 아카데미에 얼마나 진심인지를 보여주는 대목이라 생각한다. 홍보성 멘트이기도 하다. 다들 업사이드 하자.
총 4개월간 진행되는 아카데미 생활. 2개월간의 교육과정과 2개월간의 프로젝트 과정으로 나뉘어있다. 정말 밀도, 퀄리티 모두 높은 시간이었던 것 같다. 이더리움 오버뷰, 암호화폐가 가지고 있는 내재가치에 대한 수업(개인적으로 가장 좋았다), Defi, Crypto 오버뷰까지, 내가 Web3를 왜 공부하고있고, 어떠한 것을 공부하고자 하는지를 이해할 수 있도록 기초적인 부분으로 시작하였다. 이 다음으로는 Web3 “보안”아카데미답게, EVM, 그리고 Debugging 등 low level부터 EVM을 공부하기 시작하였다(C언어 배우기 전에 어셈블리 배우는 느낌이려나). 이후 AWS, k8s 등 클라우드에 대해 배우고(이 부분에서 좀 더 열심히 배웠으면 하는 후회가 남는다), 드디어 Solidity와, Smart contract vuln을 학습하면서 본격적인 Web3보안을 배웠고, 암호학을 배우며 동형암호, 서명, ZKP를 공부했다(이 부분은 사전지식 없이는 상당히 힘들었을 것 같다). 이후에는 솔라나 오버뷰, 실제 사례 fork test, 크로스체인보안 등 학습을 하였다.
업사이드 아카데미 교육기간에서 개인적으로 가장 좋았던 것은, 양질의 콘텐츠, 강의도 있겠지만 “재미있게 설계된 과제”가 학습욕구를 계속해서 끌어올려줬다. 교육기간 과제를 할 때 가장 퍼포먼스가 좋았던 것 같은데, 나름 열심히 살았고, 잘 해낸 것 같아 정말 즐겁게 수행했던 과제(?)를 몇 가지 사진으로만 나타낸다.
#1
#2
(이때 정말 행복하게 공부했던 것 같다…)
두 달간의 교육과정이 끝나고, 나머지 두 달 동안 Lending Protocol Audit을 주제로 팀프로젝트를 진행하였다. 위협모델링을 위해 정말 많은 리포트들을 읽어보며 사례조사, 공격벡터 조사, 코드분석, 테스트코드 작성 등을 진행하였고, 이를 통해 취약점을 발견해보고자 하였지만, 의미있는 취약점을 발견하지는 못한 것이 아쉬웠다.
그래도 이를 진행하면서 Lending protocol에 대한 전반적인 이해와, compound, aave, euler, venus 프로토콜 등 여러 메이저 프로토콜과, 이들을 fork한 프로토콜 또는 소규모 프로토콜은 어떻게 구성되어있는지, 그리고 use case들을 살펴본 것, 그리고 실제로 audit을 경험해본 것 등이 의미있는 활동이었다.
업사이드 아카데미를 통해 Web3, 그리고 Web3 보안에 “재미”를 붙일 수 있었다. 활동을 진행하면서 생각보다 Web3생태계가 거대하다는 것을 알았고, 탈중앙화와 기관에 대한 불신에 대해 이로부터 벗어나고자 하는 움직임이 지금의 이런 거대한 시장을 형성했다는 것이 너무나 인상적인 것 같다.
활동을 하면서, 그리고 활동 이후에 ethernaut의 모든 문제를 문제없이 모두 해결한 것에서 실력(피지컬?)이 향상된 것을 느꼈고, 이제는 규모가 큰 코드베이스를 읽을 때도 겁이 나는 것이 아닌, 새로운 것을 배울 수 있다는 기대감이 생기는 것에서 내가 정말 나에게 잘 맞는 분야를 찾아왔다는 것을 느낀다.
업사이드 아카데미 정식 과정이 종료된지 어느덧 한달 반 정도가 지났다. 아카데미 활동을 하면서 다양한 dex, defi, 그리고 multichain등 여러 개념을 접하고 경험했다고 생각한다. 하지만 이쪽 판에 있는 사람은 모두 느끼는 것 처럼 오늘 내가 A를 공부하고 있어도 다음주만 되면 언제 그랬냐는듯 다른 기술인 B가 주목받는. 너무도 빠르게 변화하면서 또 빠르게 새로운게 생기는 흥미로운 생태계이다. 마치 n년차 기술자들이 새로운 생태계에서 새로운 프로덕트를 만드는 데 신나있는 것 처럼 너무도 빠르게, 많이 새로운 것이 생긴다.
나는 이제 막 아카데미를 졸업한. Web3 생태계에서는 각 고등학교를 졸업한 것 같다. 고등학생들이 과학을 공부할 때 과학적 개념을 이해는 (어느정도) 하고 있지만, 어떤 산업이 존재하는지 세상물정을 “잘”알지는 못하듯, 지금의 나도 Web3에서는 같은 위치에 있다는 생각을 한다. 그래서 기술적인 이해를 넘어, 생태계 트렌드를 이해하고 현재 어떠한 기술이 각광받고 있는지에 대한 것을 이해하고, 또 트렌드에 발맞추기 위한 “생태계 공부”를 한달 반동안 계속 진행한 것 같다.
스테이블코인, 펌펀, 하이퍼리퀴드, AA, NFT, AI, AI-agent, 그리고 어제까지는 TEE… 그 잠깐의 시간동안 너무 많은 것들이 훅훅 지나가고 또 거치는 중에 있는 것 같다. 이 모든게 신기하면서, 그 정보량에 압도당하는 기분이다. $ENA, pump.fun, $HYPE. $PENGU, pudgy penguin, ai16z, eliza, virtuals, sendAI, pha, ata, pond 등등… 이렇게 나열해보니 정말 어질어질한 것 같다. 한달 반이라는 시간동안 이 모든걸 접했다는 사실이.
앞으로 또 어떤 새로운 기술과 메타가 기다리고있을지 기대가 되면서도, 무섭기도 하다(ㅋㅋ..). 이제 곧 군입대를 하는데, 훈련소에 가 있을 동안 또 얼마나 많은 것들이 등장할지, 다시 Web3를 접했을 때 지금 내가 알고있는 Web3 생태계와 다를 수 있겠다는 생각이 들기도 한다.
그럼에도 불구하고 계속해서 새로운 것들이 생겨나고, Web2에서 이루어질 수 없는 여러 요소들이 Web3에서 가능해지는 것을 보며, 그리고 계속해서 온체인 트잭이 늘어나는 것을 보며 ‘40살쯤 되면 이걸로 돈 많이 벌겠다~’ 라는 생각을 하게되는 것 같다.
Compound v2를 구성하는 주요 기능에는 mint, redeem, borrow, repay, liquidate가 있다. 이러한 기능들이 어떻게 작동하는지, 더 나아가 어떠한 방식으로 구현되어있는지 등을 살펴보려 한다.
이번 편에서는 Compound에서 자금을 관리하는 cToken을 주요 기능을 살펴보려 한다. 중간중간 등장하는 추가적인 개념들은 이 글에서 가볍게 다루고, 추후에 더 자세히 살펴보는 것이 좋을 것 같다.
각 기능에서는 CErc20/CEther 컨트랙트에서 상속받는 CToken 컨트랙트의 CToken::~Internal() 함수를 호출하고, 이는 CToken::~Fresh() 함수를 호출하며, 또 이는, Comptroller::~Allowed() 함수 및 Comptroller::~Verify()->Optional함수를 호출하는 흐름을 이해하고 있으면 더욱 편할 것 같다.
Mint(uint mintAmount)
이전 포스팅에서도 언급했던 것과 같이, mint(uint mintAmount)함수는 CErc20/CEther 컨트랙트에 구현되어있으며, cToken 컨트랙트의 mintInternal(uint mintAmount) 함수를 호출하여 실제 로직을 실행한다.
1. CErc20 & CEther::mint()
mint를 하기 위한 entry point인 CErc20과 CEther를 살펴보자.
function mint() external payable {
mintInternal(msg.value);
}
CEther는 네이티브 Token을 처리하기 위한, CErc20은 ERC20 Token을 처리하기 위한 컨트랙트라는 정도만 이해하고 넘어가도 된다.
Logic
각각의 mint 함수에서는 상속받는 컨트랙트인 CToken의 mintInternal(mintAmount)를 호출한다.
2. CToken::mintInternal()
function mintInternal(uint mintAmount) internal nonReentrant {
accrueInterest();
mintFresh(msg.sender, mintAmount);
}
이 함수에서는 보이는 것과 같이 두 개의 주요 로직을 실행한다.
accrueInterest()
accrueInterest함수는 프로토콜이 현재 가지고 있는 token 수량, 사용자들의 borrow 총량, 준비금 등을 바탕으로 새로운 index 값을 계산, borrow 총량을 업데이트, 준비금을 업데이트 하는 로직을 수행한다. 이에 대한 자세한 로직은 다른 글을 통해 설명하였다.
해당 cToken에 대해 minter가 mintAmount만큼 mint를 할 수 있는지 여부를 리턴해주는 mintAllowed 함수를 호출한다. (이러한 목적인 것 같기는 한데, 실제로 이렇지는 않다.)
3-1. CToken::mintAllowed
function mintAllowed(address cToken, address minter, uint mintAmount) override external returns (uint) {
require(!mintGuardianPaused[cToken], "mint is paused");
// Shh - currently unused
minter;
mintAmount;
if (!markets[cToken].isListed) {
return uint(Error.MARKET_NOT_LISTED);
}
updateCompSupplyIndex(cToken);
distributeSupplierComp(cToken, minter);
return uint(Error.NO_ERROR);
}
guardian에 의해 pause되어있는지를 검사한다.해당 조건문은 이를 통해 pause되어있는지 검사하고, pause되어있다면 revert를 발생시킨다. compound에는 governance에 의해 임명된 guardian이라는 주체(wallet)이 있는데, 이들은 프로토콜상에 문제, 해킹, 또는 어떠한 사정이 있을 경우에 특정 cToken market의 기능을 pause할 수 있는 권한을 가지고 있다.
compound만의 특징이라고 할 수 있을 것 같은데, ~Allowed 함수 내에 로직이 비어있는 것이 보인다. 추후 업데이트 시에 인터페이스가 변하지 않게 하려고 minter와 mintAmount를 함수 arg에 넣기만 하고 이용하지는 않은 것으로 보인다.
인자로 넣은 cToken의 주소가 market에 listing되어있는지 검사하여 입력값 검증을 실행하고 있는 것을 확인할 수 있다.
updateCompSupplyIndex(cToken), distributeSupplierComp(cToken, minter) 을 통해 supplier에게 돌아가는 보상을 업데이트해준다. 상세한 내용은 보상 관련 포스팅을 추후 업로드 하도록 하겠다.
mintFresh() - 2
if (accrualBlockNumber != getBlockNumber()) {
revert MintFreshnessCheck();
}
accrualBlockNumber 즉, accrueInterest()함수에서 이자율 및 관련 state변수들을 업데이트한 block number가 현재의 block.number와 같지 않다면 revert를 실행한다. 즉, accrueInterest함수를 실행하지 않고는 mintFresh()를 실행할 수 없다는 것.
totalSupply가 0보다 클 때 totalSupply가 0보다 크면, cToken이 발행된 적이 있는 즉, mint수량이 있다는 말이다. 이 경우 exchangeRate는 $\frac{totalCash + totalBorrows-totalReserves}{totalSupply}$가 된다. 즉, 프로토콜의 전체 자산(보유중인 token + borrow된 token - 준비금)을 총 cToken발행량으로 나눈 값이다.
totalSupply가 0일 때 totalSupply가 0이라는 것은, cToken 발행량이 0이라는 것이다. 즉, market에 유동성이 존재하지 않는 것이기 때문에, initialExchangeRateMantissa를 반환해준다. cETH의 intialExchangeRateMantissa값은 $2 \times 10^18$ 이다.
이제, doTransferIn(safeTransferFrom을 실행한다)을 통해 minter로부터 mintAmount만큼의 underlying token을 송금받고, 송금받은 token 수량인 actualMintAmount 을 exchangeRate로 나누어 mint 즉, 공급해야 할 cToken수량을 계산한다.
Compound v2에서 사용되는 여러 helper function들의 로직을 정리한 포스팅이다. 사실 Lending 프로토콜에서 가장 중요한 것은 사용자들이 직접 호출하는 entry point라고도 할 수 있겠지만, 실제로 사용자들의 자금을 관리, 이자를 업데이트, 프로토콜에 대한 관리 등을 수행하는 것은 이러한 helper function들이 있기에 가능한 것이기 때문에 따로 글을 빼 포스팅을 작성하였다.
목차 (Helper functions)
CToken::accrueInterest()
… (계속해서 추가해나갈 예정이다)
CToken::accrueInterest()
function accrueInterest() virtual public returns (uint);
역할
cToken의 borrow 및 deposit 자산에 대해 시간이 지남에 따라 발생하는 이자를 계산하고, 이를 기반으로 잔액, 이자율 및 관련 지표(borrowIndex, totalBorrows)를 최신 상태로 업데이트하는 역할을 한다.
주요 기능들에서 로직을 실행하기 이전에 accrueInterest 함수를 실행하면서 시작한다.
변수 Update
accrualBlockNumber
borrowIndex
totalBorrows
totalReserves
로직 deep dive
accrueInterest() - 1
function accrueInterest() public returns (uint) {
uint currentBlockNumber = getBlockNumber();
uint accrualBlockNumberPrior = accrualBlockNumber;
if (accrualBlockNumberPrior == currentBlockNumber) {
return NO_ERROR;
}
ethereum mainnet의 compound v2는 block.timestamp가 아닌, block.number를 기반으로 이자 등 관련 지표를 관리한다. 따라서 이전 업데이트 이후 발생한 이자를 계산하기 위해 현재 block.number값을 currentBlockNumber에 받아온다.
전역으로 저장된 가장 최근에 Index가 업데이트된 block.number를 accrualBlockNumberPrior변수에 저장한다.
currentBlockNumber == accrualBlockNumberPrior 를 비교하여 같다면 return을 시켜준다. block number를 기반으로 state 업그레이드가 발생하기 때문에 같은 block에서 이미 update가 되었다면 여러 index들을 새로 업데이트해줄 필요가 없기 때문.
문득, (3)의 내용이 gas를 얼마나 줄여주는지 궁금해져 foundry testcode를 작성해 테스트를 진행해보았다.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {Test, console} from "forge-std/Test.sol";
contract CounterTest is Test {
testCompoundV2Gas target;
function setUp() public {
vm.createSelectFork("eth_mainnet");
target = new testCompoundV2Gas();
}
function test_compoundV2GasOp() public {
console.log(target.getGasAmount());
console.log(target.getGasAmount());
}
}
contract testCompoundV2Gas {
// cETH address
address cETH= 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5;
function getGasAmount() public returns (uint gasUsed) {
gasUsed = gasleft();
(bool suc, ) = cETH.call(abi.encodeWithSignature("totalBorrowsCurrent()"));
require(suc, "function call failed");
gasUsed = gasUsed - gasleft();
}
}
Result
test 결과를 통해 알 수 있듯, 약 40000 wei 즉, $0.7884 (24.11.16 기준)의 gas 최적화가 되는 것을 알 수 있다.
여러 변수들을 업데이트 해주기 위해 compound v2는 현재의 underlying balance, 빌린 금액, 준비금 총액을 이용해 borrowRate를 계산해주게 된다.
interestRateModel::borrowRate()함수를 살펴보자.
function getBorrowRate(uint cash, uint borrows, uint reserves) public view returns (uint) {
uint ur = utilizationRate(cash, borrows, reserves);
return (ur * multiplierPerBlock / BASE) + baseRatePerBlock;
}
utilizationRate 즉, 활용율을 계산한 뒤 여러 가중치를 반영한 borrowRate를 리턴해주는 함수이다.
활용율은 전체 예치(deposit)된 자산 중, borrow에 쓰인 비율을 나타내는 비율이라고 알면 된다. 아래 코드를 보면 바로 이해가 될 것이다.
function utilizationRate(uint cash, uint borrows, uint reserves) public pure returns (uint) {
// Utilization rate is 0 when there are no borrows
if (borrows == 0) {
return 0;
}
return borrows * BASE / (cash + borrows - reserves);
}
이러한 활용율에 multiplierPerBlock(자산 이용율에 이자율의 증가 속도를 제어하는 상태변수) 를 곱한 후, 최소 이자율인 baseRatePerBlock을 더해주어 최종적으로 borrowRate를 반환해준다.
이렇게 얻은 borrowRate는 borrowRateMax값보다 작아야함을 검사하며 조건검사를 끝낸다.
1편과 다소 순서가 뒤바뀐 면이 없지않아 있는 것 같긴 한데, 1편의 내용은 compound에서 사용되는 핵심적인 개념들에 대한 설명이었기 때문에 필수적이었고, 이 또한 전체적인 구조를 파악할 수 있기 때문에 필수적인 것 같다. 그래서 어느 것이 먼저인지는 중요하지 않은 것 같다. 이번 편에서 전체적인 구조를 살펴보고, 다음편 부터 코드레벨까지 뜯어보며 프로토콜 전반에 대한 내용을 살펴보도록 하자.
프로토콜에 예치(또는 공급)된 자산을 EIP-20표준에 맞춰 표현한 개념으로, Compound v2의 핵심 개념이다.
그래서 이걸로 뭐하는데
cToken은 사용자가 공급한 자산의 가치를 나타낸다. 이를 통해 이자를 얻을 수 있다(예금)
cToken을 담보로 대출 등 작업을 수행할 수 있다. (대출)
cToken은 Compound와 user가 상호작용하는 주요 수단이다.
사용자가 담보 입금(mint), 출금(redeem), 대출(borrow), 상환(repay), 청산(liquidate), 또는 전송(transfer)을 할 때 cToken 컨트랙트를 통해 이루어진다.
EIP-20표준을 따른다는 것을 통해 알 수 있듯, 내가 입금한 담보자산을 제3자에게 전송하여 제3자가 이를 이용해 대출 등의 작업을 할 수 있기도 하다.
CErc20/CEther
이들은 각각 cToken컨트랙트를 상속받는 user entry point이다. 컨트랙트의 주요 기능에 대한 Overview를 살펴보자.
functions
visibility
description
mint
external
담보 입급하여 cToken발행
redeem
external
cToken소각하여 담보 인출(cToken기준)
redeemUnderlying
external
cToken소각하여 담보 인출(underlying기준)
borrow
external
cToken을 담보로 대출
repayBorrow
external
대출받은 자산을 상환
repayBorrowBehalf
external
A가 대출받은 자산을 B가 상환
liquidateBorrow
external
A가 대출받은 자산을 C가 청산(청산보너스 get)
sweepToken
external
underlying이 아닌 token이 컨트랙트에 존재하면 해당 token을 sweep
doTransferIn
internal
EIP20 transferFrom
doTransferOut
internal
EIP20 transfer
두 컨트랙트 모두 위와 같은 기능들을 제공한다. 차이는 오직 Native ETH를 다루는가, ERC20 token을 다루는가의 차이.
예시
CErc20CEther
cToken flow
end-user가 어떠한 entry point를 통해 프로토콜과 상호작용하는지 알아봤으니, cToken의 로직들이 어떻게 동작하는지 알아보자.
출처: https://deephigh.gitbook.io/deephigh
위의 그림을 보며, 예시로 mint함수를 들어 따라가보도록 하자. (다른 함수들도 다 똑같다.)
cErc20/CEther를 통해 user가 mint(uint mintAmount)를 호출하면,
mint는 internal call을 통해 mintInternal(mintAmount)를 호출한다.
mintInternal는 accrueInterest()를 통해 다음 네 가지 state variable을 업데이트한다.
borrowIndex: 이자계산에 사용되는 Index 업데이트
totalBorrows: borrow된 총량 업데이트
totalReserves: 준비금 업데이트
accrualBlockNumber: 마지막으로 위의 값들을 업데이트한 block number
업데이트 이후, mintFresh를 통해 mint작업에 들어간다.
comptroller.mintAllowed(address(this), minter, mintAmount)를 통해 minter가 mintAmount만큼 mint할 수 있는지 체크하고, CompSupplyIndex를 업데이트하며, Supplier들에게 comp를 distribute한다.
이후, 실제mint와 관련된 로직이 실행된다.
cToken의 다른 기능에 대한 로직(mint, redeem, borrow, repay)또한 위와같은 방식으로 실행된다.
하지만 liquidate에는 약간의 차이가 존재한다. 이 차이를 이해하기 위해서는 청산, 나아가 Compound의 청산에 대한 이해가 조금 필요하니 간단하게 짚고 넘어가보자.
liquidate 핵심만 빠르게
function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral)
청산(liquidate)은 대출자(borrower)의 담보를 “싸게 파는”행위이며, 이를 통해 프로토콜의 재정적 안정성을 유지하고자 하는 기능이다. (처음 들으면 무슨말인지 잘 이해가 안된다. 일단 계속 읽자)
borrower가 대출(borrow)을 할 때, 담보자산(cToken)들의 총 가치가 borrow하고자 하는 자산의 총 가치보다 커야한다.
담보자산의 가치가 빌린 자산의 가치보다 작아지는 현상(shortfall)이 발생하면, 청산의 대상이 된다.
**청산인(liquidator)**은 이를 발견하고, cToken마켓에 liquidateBorrow를 실행하여 borrower의 **담보자산(cTokenCollateral)**을 청산한다.
이 때, repayAmount만큼의 “빌린 자산”을 청산하게 되고, repayAmount만큼의 가치에 할인율(liquidationIncentiveMantissa)을 곱한 수량의 담보자산(cTokenCollateral)을 그 대가로 받아 청산에 대한 보상을 얻게 된다.
그래서 liquidate는 어떤 flow로?
사실 flow자체는 비슷할 수 있는데, 위의 내용들을 토대로 하여, borrower가 빌린 자산과, 청산의 대상이 되는 borrower가 담보로 맡긴 자산 모두에 대해 이자율 업데이트 등 작업을 수행해야하는 것을 알 수 있다. 따라서, 그러한 차이가 존재한다.
실제 liquidate 로직 그리고 cToken 기능들에 대한 전반적인 세부사항은 다음에 올릴 포스팅에서 함께 확인해보도록 하자.
Comptroller
comptroller의 사전적 정의를 살펴보면, 재정 관리자라는 뜻으로 주로 사용되는 것 같다. Compound에서도 마찬가지이다.
Comptroller는 Compound v2의 리스크 관리계층으로, 사용자가 유지해야 할 담보비율을 결정하고, 사용자의 재무건전성을 판단한다. 재무건전성은 청산과 직접적으로 연관되며 사용자가 청산될 수 있는지, 그리고 얼마나 청산될 수 있는지를 판단하는 데 사용된다.
또한 사용자가 cToken과 상호작용할 때 마다 거래가 타당한지를 검토하는 데 사용된다.
Comptroller는 업그레이드 가능한 컨트랙트로, Unitroller라는 별도의 프록시컨트랙트에서 ComptrollerStorage와 Comptroller를 관리한다.
이제, 어떠한 기능들을 수행하는지 전체적으로 살펴보도록 하자.
Comptroller
enter/exitMarket(s)
function enterMarkets(address[] calldata cTokens)
함수 이름 그대로 market(들) 목록에 들어가는 함수이다. 특정 market에 예치한 자산이 담보로 인정되도록 하거나, 특정 market에서 자산을 borrow하기 위해 enterMarket을 해야 한다.
반대로 exitMarket은 이름 그대로 특정 market에서 exit하는 함수이다.
Allowed Functions
cToken컨트랙트에서 각종 Fresh함수들(mintFresh, redeemFresh, ….)은 내부적으로 accrueInterest()를 호출한 이후, comptroller의 ~Allowed 함수(mintAllowed, redeemAllowed, …)를 호출하게 된다. ~Allowed 함수에서는
해당 기능에 들어가는 여러 Input value를 검증하고
updateCompSupplyIndex() 를 통해 보상과 관련된 Index를 업데이트하며
distributeSupplierComp() 를 통해 supplier에게 comp를 분배한다.
Verify Functions
cToken컨트랙트에서 각종 Fresh함수들(mintFresh, redeemFresh, ….)은 내부적으로 ~Allowed 함수를 호출해 Input값들을 검증한 후, 함수 로직을 수행한 다음, 마지막 단계에서 ~verify함수를 호출한다.
현재 verify함수는 사용되지 않지만, 추후에 다시 사용될 수도 있기에.. 일단 기록한다.
mintFresh() 내부로직
각종 변수 및 유틸리티
collateralFactor이 값은 Governance에 의해 증/감 할 수 있다.
기능이라기보다는 값에 가까운데, 공급한 담보자산의 실제 담보가치(대출 한도)의 비율을 나타내는 값이다. 대형자산(ETH 등), 유동성 높은 자산은 collateralFactor가 크고, 반대는 작다. 0%가 되면 해당 자산은 담보로서 활용할 수 없지만 borrow는 가능하다.
getAccountLiquidity
특정 계정이 사용할 수 있는 유동성의 총 USD가치를 계산한다. 기본적으로 enterMarket을 통해 listing되어있는 담보 및 대출 자산에 대해서만 그 값을 계산한다. 또한, 사용할 수 있는 자산의 총량이 0보다 작아지면 shortfall이 생겨 해당 계정은 청산 가능한 상태에 도달하게 되고, 이에 따라 shortfall값도 리턴해준다.
Governance
Compound는 COMP토큰 보유자가 세 가지 컨트랙트(COMP, Governor Bravo, Timelock)를 컨트롤하며 관리 및 업그레이드된다.
이 컨트랙트들은 커뮤니티가 propose, vote를 통해 cToken, Comptroller의 관리기능을 통해 변경사항을 실행할 수 있도록 한다.
또한 COMP토큰 holder는 자신의 투표권을 다른 주소에 delegate할 수 있어 25,000 COMP이상 delegatae받은 주소는 제안을 propose할 수 있고, 누구든 100 COMP를 lock하여 propose를 생성할 수 있다.
Governance Timeline
출처: Compound
최초 Propose시, 2일간의 review기간이 주어지며,
이후 투표 가중치가 기록되고 투표가 시작된다.
투표는 3일간 진행되고,
다수이면서 최소 400,000표가 찬성하면 Timelock에 pending상태로 추가되고,
해당 propose는 2일 뒤에 실행된다.
결과적으로, 프로토콜의 변경 사항은 최소 일주일이 소요된다.
Price Oracle
프로토콜의 여러 자산 가격 데이터를 Chainlink Price Feed를 통해 제공받는다.
실제 은행에서도 그렇지만, web3 Defi에서도 이자율과 이자는 정말 중요하다. 사용자들이 납부해야 할 이자와, 예치자들에게 돌려줄 수익이 보장되고, 정확히 계산되어야 시장이 건전하게 돌아갈 수 있으니 말이다.
그렇다면, 이러한 이자는 어떻게 업데이트해주는걸까? web3에서는 모든 계산이 user가 호출하는 로직에서 수행되고, 매 호출마다 supply, borrow를 한 모든 user의 이자가 업데이트된다면 user들은 프로토콜을 이용하려하지 않을 것이다. (gas fee감당불가) 이를 해결하기 위해, compound에서는 index라는 개념을 통해 이자를 누적시킨다. index가 어떻게 사용되는지, 그리고 어떻게 계산되는지를 알아보자.
계산 로직
function accrueInterset() {
...
borrowRate = interestRateModel.getBorrowRate(cash, borrows, reserves)
...
simpleInterestFactor = borrowRate * blockDelta // 마지막 업데이트 이후 지난 블록 수
borrowIndex = borrowIndex * (simpleInterestFactor + 1)
...
}
baseRatePerYear: The approximate target base APR, as a mantissa (scaled by BASE) : 연이자율
multiplierPerYear: The rate of increase in interest rate wrt utilization (scaled by BASE) : Utilization 가중치
function getBorrowRate(cash, borrows, reserves) returns (borrowRate){
ur = borrows * 1e18 / (cash + borrows - reserves)
borrowRate = ur * multiplierPerBlock / BASE + baseRatePerBlock
}
말로 풀어보자.
우선, borrowRate는 baseRate에 utilizationRate에 multiplier를 곱한 값을 더한 결과이다.
utilizationRate(ur)는 활용률 즉, 프로토콜에 예치된 자산 중 대출에 활용된 자산의 비율을 말하며, 활용이 많이 되고 있다는 것은 해당 자산이 많이 사용된다는 말이므로, 이에 대한 borrowRate가 커지게 된다.
이를 통해 도출된 borrowRate와 마지막 업데이트 이후 지난 블록 수(blockDelta)를 곱해 simpleInterestFactor를 계산하고, 현재 borrowIndex에 이 값을 곱한 후 borrowIndex에 더하여 업데이트한다.
이러한 방식으로 borrowIndex는 처음 세팅 이후, 예치된 자산의 활용율에 따라 점점 커지는 방식이라고 생각할 수 있다.